Row Polymorphism and Extensible Records and Variants
A record is a finite collection of values tagged by distinct names, called labels; its field at label ℓ is retrieved by the selector 𝑟.ℓ. Mathematically, a closed record is an element of the finite indexed product of its field types: it chooses one value at every label. A variant instead chooses one label and one value of the type associated with that label. The corresponding sum of alternatives is called the indexed coproduct of the field types. A row is the finite partial map from labels to component types that indexes either construction.
A record selector is easy to type when the whole record is known. For example, 𝜆𝑟.𝑟.𝗉𝗈𝗋𝗍:𝖱𝖾𝖼({𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀,𝗉𝗈𝗋𝗍:𝖭𝖺𝗍})→𝖭𝖺𝗍. But this type fixes something that the program does not use: the selector works just as well when the record has a timeout, a log handle, or fifty other fields. Repeating one type for every possible remainder is not a solution, and ordinary Hindley–Milner substitutions replace type variables by types, not by rows of fields.
Extension makes the obstruction sharper. We want one principal type for 𝗐𝗂𝗍𝗁𝖲𝖾𝖼𝗎𝗋𝖾:=𝜆𝑟.{𝗌𝖾𝖼𝗎𝗋𝖾=𝗍𝗋𝗎𝖾∣𝑟}, and we want the result to retain every field of 𝑟. A type variable for the entire argument loses those fields; a fixed record type chooses them in advance. The missing object is a variable ranging over the row of untouched fields.
The same objects occur outside software records. A measurement result can be a record of named channels, while a tagged experimental outcome is a variant; row polymorphism lets a calculation mention the channels or outcomes it uses while remaining uniform in the unmentioned part of the index family.
Rows are finite maps: each label occurs at most once, and field order is irrelevant. The judgment 𝜌\ℓ proves that extending 𝜌 at ℓ preserves this invariant. Rows with duplicate labels and first-match selection obey a different equality.
Rows and the strictness invariant
Fix a countably infinite set of labels. Labels are written ℓ,𝑚,𝑛; names such as 𝗁𝗈𝗌𝗍 and 𝗉𝗈𝗋𝗍 are particular labels. Type variables 𝛼,𝛽,… and row variables 𝜉,𝜁,… are disjoint countable supplies.
Types and rows are formed simultaneously by 𝜏::=𝛼∣𝖭𝖺𝗍∣𝖡𝗈𝗈𝗅∣𝖲𝗍𝗋𝗂𝗇𝗀∣𝜏→𝜏∣𝖱𝖾𝖼(𝜌)∣𝖵𝖺𝗋(𝜌),𝜌::=𝜉∣𝜖𝜌∣{ℓ:𝜏∣𝜌}. Here 𝜖𝜌 is the empty row. A lacks predicate is 𝜌\ℓ, read as the assertion that row 𝜌 lacks label ℓ. The backslash is a relation between a row and a label, not row subtraction or set difference. A finite set of predicates is denoted 𝑃. Type substitutions send type variables to types and row variables to rows, and act structurally on both sorts and on predicates. A row substitution is allowed only when it sends every assumed lacks fact to a true lacks fact; otherwise substitution could create a duplicate label.
The four operations that later share a minus-like glyph have different input and output sorts: 𝜌\ℓlacksproposition𝜌−ℓmetaleveldeletionfromafiniterow𝜌−residualrowreturnedbyconstrainedinsertion𝑒−ℓsourcetermrestrictingarecord.
The braces in {ℓ:𝜏∣𝜌} construct a row, not a record type; 𝖱𝖾𝖼 and 𝖵𝖺𝗋 turn the same row into a record type and a variant type. Thus 𝖵𝖺𝗋 abbreviates variant; it never denotes a term or row variable. For a closed row 𝜌, these types denote respectively the indexed product and indexed coproduct over dom(𝜌). We abbreviate nested extensions by {ℓ1:𝜏1,…,ℓ𝑘:𝜏𝑘∣𝜌},{ℓ1:𝜏1,…,ℓ𝑘:𝜏𝑘}:={ℓ1:𝜏1,…,ℓ𝑘:𝜏𝑘∣𝜖𝜌}.
The grammar alone permits {ℓ:𝜏,ℓ:𝜐∣𝜌}. The formation judgment now excludes it.
The function 𝗌𝗂𝗆𝗉 scans a row until it finds ℓ, reaches the empty row, or reaches an open tail. It is defined by structural recursion on the row argument: 𝗌𝗂𝗆𝗉(𝜖𝜌\ℓ)=⊤,𝗌𝗂𝗆𝗉({𝑚:𝜏∣𝜌}\ℓ)=𝗌𝗂𝗆𝗉(𝜌\ℓ)(𝑚≠ℓ),𝗌𝗂𝗆𝗉({ℓ:𝜏∣𝜌}\ℓ)=⊥,𝗌𝗂𝗆𝗉(𝜉\ℓ)=𝜉\ℓ. The row-variable clause returns a normal form; 𝗌𝗂𝗆𝗉 is a total function, not a term-reduction relation. For a set, apply it to every predicate, discard ⊤, fail at ⊥, and remove duplicates. Write 𝗇𝖿(𝑃) for the resulting set. A predicate context is always normalized: every member therefore has form 𝜉\ℓ. In particular it is satisfiable, by sending every row variable to 𝜖𝜌. For example, 𝗌𝗂𝗆𝗉({𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜉}\𝗉𝗈𝗋𝗍)=𝜉\𝗉𝗈𝗋𝗍. For a one-predicate calculation, write 𝑝⇓𝑞 when 𝗌𝗂𝗆𝗉(𝑝)=𝑞.
For normalized 𝑃, the entailment 𝑃⊩𝜌\ℓ is generated by
𝜉\ℓ∈𝑃
𝑃⊩𝜉\ℓ
L-Assume
𝑃⊩𝜖𝜌\ℓ
L-Empty
𝑃⊩𝜌\ℓ𝑚≠ℓ
𝑃⊩{𝑚:𝜏∣𝜌}\ℓ
L-Extend
For normalized predicate contexts, write 𝑄⊩𝑃 when 𝑄 entails every predicate in 𝑃.
For example, the two rule applications needed to pass a displayed field distinct from the requested label are 𝜉\𝗉𝗈𝗋𝗍∈𝑃𝑃⊩𝜉\𝗉𝗈𝗋𝗍L−Assume𝗁𝗈𝗌𝗍≠𝗉𝗈𝗋𝗍𝑃⊩{𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜉}\𝗉𝗈𝗋𝗍L−Extend.
An admissible substitution𝑆:𝑃→𝑄 is a finite, sort-preserving substitution for which every nonidentity type image is a 𝑄-formed type, every nonidentity row image is a 𝑄-formed strict row, 𝗇𝖿(𝑃[𝑆]) exists, and 𝑄⊩𝗇𝖿(𝑃[𝑆]). Thus the target may contain more information than substitution alone produces, but it may not license an ill-formed image. All substitution statements below use admissible substitutions; an attempted substitution whose normalized context is ⊥ is rejected rather than treated as a map of strict rows. The notation 𝑆:𝑃→𝑄 records the source and target predicate contexts of the substitution. It is not a function type inside the object language.
Write 𝑃⊢𝜌𝗋𝗈𝗐 for strict row formation. The empty row and every row variable are rows. Type variables and the three base types are types; arrows are formed componentwise. The row-specific rules are
𝑃⊢𝜉𝗋𝗈𝗐
Row-Var
𝑃⊢𝜖𝜌𝗋𝗈𝗐
Row-Empty
𝑃⊢𝜌𝗋𝗈𝗐𝑃⊢𝜏𝗍𝗒𝗉𝖾𝑃⊩𝜌\ℓ
𝑃⊢{ℓ:𝜏∣𝜌}𝗋𝗈𝗐
Row-Ext
The remaining type-formation rules are 𝑃⊢𝜏𝗍𝗒𝗉𝖾𝑃⊢𝜐𝗍𝗒𝗉𝖾𝑃⊢𝜏→𝜐𝗍𝗒𝗉𝖾Ty−Arrow𝑃⊢𝜌𝗋𝗈𝗐𝑃⊢𝖱𝖾𝖼(𝜌)𝗍𝗒𝗉𝖾Ty−Record𝑃⊢𝜌𝗋𝗈𝗐𝑃⊢𝖵𝖺𝗋(𝜌)𝗍𝗒𝗉𝖾Ty−Variant.
The definitions are well founded. Simplification and lacks entailment recurse on the row tail; strict formation also recurses on a smaller tail and uses, but never defines, lacks entailment.
For example, 𝜉\𝗉𝗈𝗋𝗍⊩{𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜉}\𝗉𝗈𝗋𝗍, because the assumption is the tail judgment 𝜉\𝗉𝗈𝗋𝗍 and 𝗁𝗈𝗌𝗍≠𝗉𝗈𝗋𝗍 is the side condition of L-Extend. Hence 𝜉\𝗉𝗈𝗋𝗍⊢{𝗉𝗈𝗋𝗍:𝖭𝖺𝗍,𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜉}𝗋𝗈𝗐. By contrast, formation of {𝗉𝗈𝗋𝗍:𝖡𝗈𝗈𝗅,𝗉𝗈𝗋𝗍:𝖭𝖺𝗍∣𝜉} would require a derivation of {𝗉𝗈𝗋𝗍:𝖭𝖺𝗍∣𝜉}\𝗉𝗈𝗋𝗍, and no rule can produce one.
Rows are unordered, but only distinct labels may pass one another.
Row equality=𝗋 on types and rows is the least two-sorted congruence containing {ℓ:𝜏,𝑚:𝜐∣𝜌}=𝗋{𝑚:𝜐,ℓ:𝜏∣𝜌}(ℓ≠𝑚), without a formation side condition. Under a predicate context 𝑃, write 𝑎≡𝑃𝑎′ when both expressions have the same sort, are well formed under 𝑃, and 𝑎=𝗋𝑎′. Thus row equality permutes distinct fields, and type equality transports those permutations through 𝖱𝖾𝖼, 𝖵𝖺𝗋, arrows, and field types.
If 𝑎 is a type or row formed under 𝑃 and 𝑎=𝗋𝑎′, then 𝑎′ is formed under 𝑃. More precisely, the raw equality can be represented by a finite chain of adjacent exchanges in constructor contexts, and every expression in that chain is formed under 𝑃.
Type equality preserves and reflects outer constructors. In particular, 𝜏1→𝜏2≡𝑃𝜐1→𝜐2⟹𝜏1≡𝑃𝜐1and𝜏2≡𝑃𝜐2,𝖱𝖾𝖼(𝜌)≡𝑃𝖱𝖾𝖼(𝜌′)⟹𝜌≡𝑃𝜌′,𝖵𝖺𝗋(𝜌)≡𝑃𝖵𝖺𝗋(𝜌′)⟹𝜌≡𝑃𝜌′. No arrow, record, variant, base type, or type variable is equal to a type with another outer constructor; equal base types and type variables are identical.
Proof of Lemma 7.6 — Formation and constructor separation
Proof. Flatten a raw-equality derivation into a chain. Reflexivity gives the empty chain, symmetry reverses a chain, transitivity concatenates two chains, and congruence places every link in the same constructor context. Thus each link changes only one adjacent pair {ℓ:𝜏,𝑚:𝜐∣𝜌}↔{𝑚:𝜐,ℓ:𝜏∣𝜌},ℓ≠𝑚, possibly below constructors.
One such link preserves formation. Formation of the left row says that 𝜌 lacks 𝑚 and that {𝑚:𝜐∣𝜌} lacks ℓ. Since ℓ≠𝑚, the latter premise reduces to the fact that 𝜌 lacks ℓ. These two facts build the right row in the opposite order. The reverse direction is the same calculation. If the exchange occurs inside a tail, the same calculation is performed there and each enclosing Row-Ext is rebuilt; its lacks premise is unchanged because an exchange of labels distinct from the queried label merely changes the order of two L-Extend steps. Type constructors and field types rebuild structurally. Induction along the flattened chain proves item 1, including formation of every intermediate expression.
An exchange has row sort. Placing it in a type context can change only the components below that fixed context; it cannot change the outer type constructor. Follow the chain and project its links into the two arrow components or the single 𝖱𝖾𝖼 or 𝖵𝖺𝗋 component. The projected chains are the equalities displayed in item 2. A base type or type variable has no row-containing subexpression, so its chain is empty. ◻
Proof. It suffices to inspect one adjacent exchange in its constructor context. If the exchanged pair belongs to the row whose lack of ℓ is being proved and either exchanged label is ℓ, neither side has a lacks derivation. If both labels differ from ℓ, two uses of L-Extend remove them in either order and leave the same tail judgment. An exchange inside a field type leaves that row unchanged. An exchange in its tail is handled by the induction hypothesis before rebuilding the identical outer L-Extend steps. Congruence, symmetry, and transitivity preserve the resulting equivalence. ◻
Thus {𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀,𝗉𝗈𝗋𝗍:𝖭𝖺𝗍∣𝜉}≡𝑃{𝗉𝗈𝗋𝗍:𝖭𝖺𝗍,𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜉}, provided 𝑃⊩𝜉\𝗁𝗈𝗌𝗍 and 𝑃⊩𝜉\𝗉𝗈𝗋𝗍. The order has changed; the label-to-type association has not.
Every displayed label occurs at most once before any row-variable tail.
Two rows with the same tail variable (or both with empty tail) are equal exactly when their displayed fields determine the same finite map from labels to types, with field types compared by type equality.
If ℓ:𝜏 occurs in 𝜌, there is a row 𝜌−ℓ, unique up to ≡𝑃, such that 𝜌≡𝑃{ℓ:𝜏∣𝜌−ℓ}.
Proof of Lemma 4.5 — Finite-map character of strict rows
Proof. For item 1, induction on formation. The premise of Row-Ext says the tail lacks the new label. For item 2, adjacent exchanges of distinct labels generate every permutation of a finite list, so equal finite maps give equal rows. Conversely, one exchange preserves the finite map; induction on an equality derivation proves the other direction.
For item 3, scan the displayed fields. At the desired label, delete its field. Before reaching it, exchange it leftward past each distinct label. Item 1 guarantees that there is only one field to delete. Any two scans leave the same finite map and tail, hence item 2 makes their results equal. ◻
The tempting equation {ℓ:𝜏,ℓ:𝜐∣𝜌}≡{ℓ:𝜐,ℓ:𝜏∣𝜌} is not merely absent: neither side is a strict row. If duplicates were admitted and the equation were added, selection would have to decide which ℓ is meant. A system that retains the first matching label can be coherent, but it is a different calculus.
★☆☆ Assume 𝑃 entails that 𝜉 lacks 𝗁𝗈𝗌𝗍, 𝗉𝗈𝗋𝗍, and 𝗌𝖾𝖼𝗎𝗋𝖾. Give adjacent-exchange derivations putting each of the three labels first in {𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀,𝗉𝗈𝗋𝗍:𝖭𝖺𝗍,𝗌𝖾𝖼𝗎𝗋𝖾:𝖡𝗈𝗈𝗅∣𝜉}. Explain at which formation premise the same calculation fails if the last label is another 𝗉𝗈𝗋𝗍.
A qualified type and a qualified scheme have forms 𝜒::=𝑃⇒𝜏,𝜎::=∀¯𝛼¯𝜉.𝑃⇒𝜏. Read 𝑃⇒𝜏 as type 𝜏 subject to the assumptions 𝑃. This double arrow belongs to qualified types; the mapsto arrow in a case term separates a pattern from its branch body. The prefix binds both sorts of variables. A ground substitution 𝐺 satisfies 𝑃, written 𝐺⊧𝗀𝑃, when every 𝜌\ℓ∈𝑃 becomes a closed strict row lacking ℓ. This notation is used in the ground-discharge interface below.
A symbolic instance of 𝜎 in an ambient predicate context 𝑃′ chooses a substitution 𝑇 for exactly the quantified prefix, sends type variables to 𝑃′-formed types and row variables to 𝑃′-formed strict rows, and requires 𝑃′⊩𝗇𝖿(𝑃[𝑇]). It then has type 𝜏[𝑇]. Thus a symbolic use retains residual row variables and discharges their lacks requirements from the ambient assumptions. A ground instance additionally requires every image of 𝑇 to be closed; taking 𝑃′=∅ alone does not close a row-variable image. Generalize every type or row variable absent from Γ. A predicate mentioning a variable fixed by Γ remains outside the scheme as a residual assumption, because no use of the let-bound variable can discharge it. Formally, put 𝑋=ftv(𝑃,𝜏)∖ftv(Γ),𝑃𝗋={𝑝∈𝑃∣ftv(𝑝)⊈𝑋}. Then GenΓ(𝑃⇒𝜏):=∀𝑋.𝑃⇒𝜏, where ∀𝑋 lists all type variables first and then all row variables, using once-and-for-all orders on the two countable supplies. On type variables this is the fixed order of definition 3.12; the row-variable order extends that convention rather than replacing it. Generalization keeps all of 𝑃 in the scheme. The residual predicates 𝑃𝗋 mention variables fixed by the environment and must additionally remain assumptions of the enclosing let judgment. Thus the whole predicate set travels with the scheme, while the enclosing let also retains 𝑃𝗋 outside it; otherwise an unused binding could hide an unsatisfied ambient requirement. Here ftv is deliberately two-sorted: it collects both free type variables and free row variables.
The constraint is a condition on instantiation, not a hidden field. For example, ∀𝛼𝜉.(𝜉\𝗉𝗈𝗋𝗍)⇒𝖱𝖾𝖼({𝗉𝗈𝗋𝗍:𝛼∣𝜉})→𝛼 has an instance at every record row containing one 𝗉𝗈𝗋𝗍 field. It has no instance in which 𝜉 itself supplies another 𝗉𝗈𝗋𝗍.
The qualified judgment Γ⊢𝑞𝑒:𝑃⇒𝜏 records the constraints required by 𝑒. Contexts contain qualified schemes. We use a separate nullary signature Σ0={𝗓𝖾𝗋𝗈:𝖭𝖺𝗍,𝗍𝗋𝗎𝖾:𝖡𝗈𝗈𝗅,𝖿𝖺𝗅𝗌𝖾:𝖡𝗈𝗈𝗅}∪{"𝚜":𝖲𝗍𝗋𝗂𝗇𝗀∣𝑠isastring}. Its constants are values. Thus a closed term has empty Γ without losing the base literals, and no unmentioned arrow-valued primitive can create a stuck application.
Let 𝜎=∀¯𝛼¯𝜉.𝑄⇒𝜏0. A symbolic instance chooses such a sorted, formation-preserving substitution 𝑇 for exactly the displayed prefix. Qualified typing is generated by
Γ(𝑥)=𝜎𝑃⊩𝗇𝖿(𝑄[𝑇])𝑃⊢𝜏0[𝑇]𝗍𝗒𝗉𝖾
Γ⊢𝑞𝑥:𝑃⇒𝜏0[𝑇]
Q-Var
𝑐:𝜎isinthefixedsignature𝑃⊩𝗇𝖿(𝑄[𝑇])𝑃⊢𝜏0[𝑇]𝗍𝗒𝗉𝖾
Γ⊢𝑞𝑐:𝑃⇒𝜏0[𝑇]
Q-Const
Γ,𝑥:𝜏1⊢𝑞𝑒:𝑃⇒𝜏2
Γ⊢𝑞𝜆𝑥.𝑒:𝑃⇒𝜏1→𝜏2
Q-Lam
Γ⊢𝑞𝑒1:𝑃1⇒𝜏1→𝜏2Γ⊢𝑞𝑒2:𝑃2⇒𝜏1𝗇𝖿(𝑃1∪𝑃2)=𝑃
Γ⊢𝑞𝑒1𝑒2:𝑃⇒𝜏2
Q-App
Γ⊢𝑞𝑒1:𝑃1⇒𝜏1Γ,𝑥:GenΓ(𝑃1⇒𝜏1)⊢𝑞𝑒2:𝑃2⇒𝜏2𝗇𝖿(𝑃𝗋1∪𝑃2)=𝑃
Γ⊢𝑞𝗅𝖾𝗍𝑥=𝑒1𝗂𝗇𝑒2:𝑃⇒𝜏2
Q-Let
Γ⊢𝑞𝑒:𝑃⇒𝜏𝜏≡𝑃𝜏′
Γ⊢𝑞𝑒:𝑃⇒𝜏′
Q-Conv
Every judgment Γ⊢𝑞𝑒:𝑃⇒𝜏 presupposes that Γ is a context of 𝑃-formed schemes and that 𝑃⊢𝜏𝗍𝗒𝗉𝖾. A monotype declaration abbreviates the scheme ∀∅.∅⇒𝜏.
The six rules already type a complete qualified let before any transport lemma is needed. With 𝗂𝖽=𝜆𝑥.𝑥, the derivation 𝑥:𝛼⊢𝑞𝑥:∅⇒𝛼⋅⊢𝑞𝜆𝑥.𝑥:∅⇒𝛼→𝛼Q−Lam𝗂𝖽:∀𝛼.∅⇒𝛼→𝛼⊢𝑞𝗂𝖽𝗓𝖾𝗋𝗈:∅⇒𝖭𝖺𝗍⋅⊢𝑞𝗅𝖾𝗍𝗂𝖽=𝜆𝑥.𝑥𝗂𝗇𝗂𝖽𝗓𝖾𝗋𝗈:∅⇒𝖭𝖺𝗍Q−Let uses the 𝖭𝖺𝗍 instance at the body occurrence. A predicate-bearing let differs only in the residual set retained by Q-Let; the first such calculation follows the record rules below.
Qualified typing is declarative: it constrains instances and lets but chooses no fresh variables, work-list order, or unification strategy. Rules Q-Var and Q-Const instantiate constraints at use sites, while Q-Let is the sole generalization rule.
The opening programs force four saturated record operations before any transport theorem is required.
Extend the pure let-language of definition 3.1 by 𝑒::=⋯∣{}∣{ℓ=𝑒∣𝑒}∣𝑒.ℓ∣𝑒−ℓ. The empty record is the base case. Selection exposes a field after row conversion, and restriction retains the exposed tail:
Γ⊢𝑞{}:∅⇒𝖱𝖾𝖼(𝜖𝜌)
Q-Empty
Γ⊢𝑞𝑟:𝑃⇒𝖱𝖾𝖼({ℓ:𝜏∣𝜌})
Γ⊢𝑞𝑟.ℓ:𝑃⇒𝜏
Q-Select
Γ⊢𝑞𝑟:𝑃⇒𝖱𝖾𝖼({ℓ:𝜏∣𝜌})
Γ⊢𝑞𝑟−ℓ:𝑃⇒𝖱𝖾𝖼(𝜌)
Q-Restrict
Extension joins the constraints of its two subterms with the exact absence fact that makes the result strict:
Update is derived rather than primitive: {ℓ:=𝑒∣𝑟}:={ℓ=𝑒∣𝑟−ℓ}. If 𝑒:𝛼 and 𝑟:𝖱𝖾𝖼({ℓ:𝛽∣𝜉}), restriction returns 𝖱𝖾𝖼(𝜉) and extension returns 𝖱𝖾𝖼({ℓ:𝛼∣𝜉}) under the constraint 𝜉\ℓ. The repeated tail 𝜉 expresses preservation of every untouched field.
Proof. Induct on the typing derivation. Variable and constant leaves retain their instance entailments by transitivity. Lambda, application, and the record and variant rules use the induction hypotheses and normalization idempotence.
For Q-Let, keep the first premise Γ⊢𝑞𝑒1:𝑃1⇒𝜏1, its generalized declaration 𝜎=GenΓ(𝑃1⇒𝜏1), and the residual split fixed. The induction hypothesis weakens only the body premise from 𝑃2 to 𝑃′. Since the original conclusion has 𝑃=𝗇𝖿(𝑃𝗋1∪𝑃2) and 𝑃′⊩𝑃, every member of 𝑃𝗋1 and every member of 𝑃2 is entailed by 𝑃′. Hence 𝗇𝖿(𝑃𝗋1∪𝑃′)=𝑃′, where normalized duplicates are discarded and every residual assumption is already a consequence of 𝑃′. Reapply Q-Let with conclusion context 𝑃′. This argument does not require 𝑃′ to entail predicates that were generalized into 𝜎; those remain checked only at instances of the bound variable. For Q-Conv, lacks conversion preserves the row equalities used by the final type conversion. ◻
For a finite two-sorted set 𝑋 of variables, write 𝑆≡𝑃𝑇on𝑋 when the two substitutions have 𝑃-formed images there and 𝛼[𝑆]≡𝑃𝛼[𝑇] for every type variable 𝛼∈𝑋, while 𝜉[𝑆]≡𝑃𝜉[𝑇] for every row variable 𝜉∈𝑋. Literal list equality would be wrong: two factors may display the same strict row in different field orders.
Two scheme contexts are 𝑃-equivalent, written Γ≡𝑃Γ′, when they have the same term variables and every pair of corresponding schemes satisfies the following three conditions after common freshening:
their quantified prefixes are identical;
their bodies are raw-equal up to row permutation;
for each predicate 𝜌\ℓ in the normalized instance predicates of one scheme, the other contains a predicate 𝜌′\ℓ with the same label and 𝜌≡𝑃𝜌′, and conversely.
Formation here is qualified-scheme formation. For a declaration of the form ∀¯𝛼¯𝜉.𝑄𝜎⇒𝜏, treat its bound prefix as parameters and form every predicate row and 𝜏 under 𝗇𝖿(𝑃∪𝑄𝜎). Thus a scheme may use the very lacks facts that it records. The definition is deliberately structural rather than merely extensional at 𝑃. By lacks conversion, every symbolic instance usable at a variable leaf under 𝑃 has a 𝑃-equal instance in the other context, in both directions.
For example, under 𝑃={𝜉\𝗁𝗈𝗌𝗍,𝜉\𝗉𝗈𝗋𝗍}, the declarations Γ1=𝑥:𝖱𝖾𝖼({𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀,𝗉𝗈𝗋𝗍:𝖭𝖺𝗍∣𝜉}),Γ2=𝑥:𝖱𝖾𝖼({𝗉𝗈𝗋𝗍:𝖭𝖺𝗍,𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜉}) form 𝑃-equivalent one-variable contexts. A variable leaf may use either declaration, and conversion identifies the two resulting record types. For a predicate-bearing pair, take 𝑦:∀𝜉.(𝜉\𝗁𝗈𝗌𝗍,𝜉\𝗉𝗈𝗋𝗍)⇒𝖱𝖾𝖼({𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀,𝗉𝗈𝗋𝗍:𝖭𝖺𝗍∣𝜉}),𝑦:∀𝜉.(𝜉\𝗉𝗈𝗋𝗍,𝜉\𝗁𝗈𝗌𝗍)⇒𝖱𝖾𝖼({𝗉𝗈𝗋𝗍:𝖭𝖺𝗍,𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜉}). Normalization pairs the two 𝗁𝗈𝗌𝗍 predicates and the two 𝗉𝗈𝗋𝗍 predicates by label; each paired row is literally 𝜉.
Row permutation may change the written context without changing any symbolic instance. Admissible substitutions compose, and generalization retains the environmental lacks assumptions needed after substitution. The following transport lemmas state these facts.
Proof of Lemma 4.9 — Changing the predicate context
Proof. Work one corresponding declaration at a time, after commonly freshening its quantified prefix. Raw equality is independent of a predicate context, and it preserves every free variable. Under the common internal predicate set 𝑄𝜎, the assumed qualified 𝑄-formation therefore turns the same raw body equality into equality under 𝗇𝖿(𝑄∪𝑄𝜎). It does the same for the paired rows in the normalized instance predicates; the lacks-conversion lemma then transfers their entailment. Hence a prefix substitution is a symbolic instance of one declaration under 𝑄 exactly when it is a symbolic instance of the other, and their instance bodies are 𝑄-equal. This is Γ≡𝑄Γ′. Equality of free-variable sets follows at the same time, declaration by declaration. ◻
If Γ≡𝑃Γ′ and Γ⊢𝑞𝑒:𝑃⇒𝜏, then Γ′⊢𝑞𝑒:𝑃⇒𝜏. In particular, if 𝑆≡𝑃𝑇 on every type and row variable free in Γ, then Γ[𝑆]≡𝑃Γ[𝑇], and either image context may replace the other in a qualified typing under 𝑃.
Proof of Lemma 4.10 — Qualified typing respects equivalent contexts
Proof. Induct on the typing derivation. For a variable leaf, commonly freshen Γ(𝑥) and Γ′(𝑥). If the original symbolic instance uses prefix substitution 𝑇, the definition of context equivalence gives 𝜏𝑥[𝑇]≡𝑃𝜏′𝑥[𝑇]and𝑃⊩𝗇𝖿(𝑄𝑥[𝑇]). Thus Q-Var derives the corresponding instance under Γ′, and Q-Conv restores the original result type. Constants do not inspect the context. For every other premise with predicate context 𝑃𝑖, the formation presupposition of definition 4.7 supplies qualified 𝑃𝑖-formation derivations for every declaration used in that premise. The change lemma therefore gives Γ≡𝑃𝑖Γ′. Apply the induction hypothesis to that premise, then reapply the lambda, application, record, or variant rule.
In a let, the first induction hypothesis preserves 𝑃1 and 𝜏1. The change lemma gives ftv(Γ)=ftv(Γ′), so both generalizations choose the same prefix and residual split. Extend both contexts by this common declaration and apply the second induction hypothesis under 𝑃2. Transitivity discharges the final conversion. The last assertion follows by induction on scheme bodies; pointwise row equality is preserved by every type constructor, and the preceding lacks-conversion lemma preserves every instantiated predicate. ◻
Proof of Lemma 7.16 — Admissibility of one-variable elimination
Proof. The substitution is finite and sort preserving. Its only nonidentity image is formed and, at row sort, strict by hypothesis. Substitute [𝑎/𝑧] through its formation derivation. In the Row-Ext case, substitute through the lacks derivation as well. Each resulting assumption leaf occurs in 𝑄=𝗇𝖿(𝑃[𝐼]), so it is entailed by 𝑄. This proves that the image is 𝑄-formed. Finally 𝑄 is the normal form of the substituted source predicates, so it entails that normal form by reflexivity. These are exactly the clauses of definition 7.3. ◻
The identity is admissible 𝑃→𝑃. If 𝑆:𝑃→𝑄 and 𝑅:𝑄→𝑃′ are admissible, then 𝑆;𝑅:𝑃→𝑃′ is admissible. Moreover, substitution by 𝑅 sends every 𝑄-formation, lacks-entailment, and strict row equality derivation to the corresponding 𝑃′-derivation.
Proof of Lemma 4.11 — Identity and composition of admissible substitutions
Proof. Identity substitution leaves every formation, entailment, and strict-equality derivation unchanged. For composition, induct first on type and row formation. Every image under 𝑆 is 𝑄-formed, and the induction sends it to a 𝑃′-formed image under 𝑅. Induction on lacks entailment sends 𝑄⊩𝜌\ℓ to 𝑃′⊩𝜌[𝑅]\ℓ: assumption leaves follow from 𝑃′⊩𝗇𝖿(𝑄[𝑅]), and empty and unequal extension are preserved literally. Applying this to 𝑄⊩𝗇𝖿(𝑃[𝑆]) shows 𝑃′⊩𝗇𝖿(𝑃[𝑆;𝑅]). Adjacent exchanges retain their distinct labels, so the same induction preserves strict row equality. These are exactly the clauses in the definition of admissibility. ◻
A substituted environment may need ambient lacks assumptions to form its declarations. Let 𝐹 be the normalized lacks assumptions used in those formation derivations. Since every variable of 𝐹 occurs in the environment, generalization retains rather than quantifies them.
Let 𝜎=GenΓ(𝑃⇒𝜏). Freshen its bound prefix away from 𝑆, and write 𝑆⋅𝜎 for substitution by 𝑆 on the free variables of the scheme only. Suppose 𝐹 is normalized, every variable of 𝐹 occurs in Γ[𝑆], and 𝑃𝑆=𝗇𝖿(𝑃[𝑆]∪𝐹),𝜎𝑆=GenΓ[𝑆](𝑃𝑆⇒𝜏[𝑆]) are formed. If 𝑄 entails 𝐹, then every qualified derivation under Γ[𝑆],𝑥:𝑆⋅𝜎 and ambient predicates 𝑄 remains derivable after replacing that declaration by 𝑥:𝜎𝑆.
Proof of Lemma 4.12 — Ambient formation and generalized declarations
Proof. Induct on the derivation. Only a leaf selecting 𝑥 changes. Such a leaf chooses an instance 𝑇 of the old bound prefix and has entailment premise 𝑄⊩𝗇𝖿(𝑃[𝑆;𝑇]). The same 𝑇 is an instance of 𝜎𝑆. It fixes 𝐹, because the variables of 𝐹 occur in Γ[𝑆] and hence are not generalized. Therefore 𝑄 entails 𝗇𝖿(𝑃[𝑆;𝑇]∪𝐹)=𝗇𝖿(𝑃𝑆[𝑇]), and the result type is still 𝜏[𝑆;𝑇]. Rebuild the leaf and then the surrounding derivation. ◻
The residual set of Q-Let is a deliberate difference from unrestricted qualified-implication introduction: a constraint involving an environmental variable cannot be hidden merely because the let-bound variable is unused. Record and variant operations are saturated term forms, so progress needs no run-time status for a half-applied selector or case operator.
The definitions 𝗉𝗈𝗋𝗍𝖮𝖿:=𝜆𝑟.𝑟.𝗉𝗈𝗋𝗍,𝗐𝗂𝗍𝗁𝖲𝖾𝖼𝗎𝗋𝖾:=𝜆𝑟.{𝗌𝖾𝖼𝗎𝗋𝖾=𝗍𝗋𝗎𝖾∣𝑟},𝗐𝗂𝗍𝗁𝗈𝗎𝗍𝖯𝗈𝗋𝗍:=𝜆𝑟.𝑟−𝗉𝗈𝗋𝗍 have the displayed schemes 𝗉𝗈𝗋𝗍𝖮𝖿:∀𝛼𝜉.(𝜉\𝗉𝗈𝗋𝗍)⇒𝖱𝖾𝖼({𝗉𝗈𝗋𝗍:𝛼∣𝜉})→𝛼,𝗐𝗂𝗍𝗁𝖲𝖾𝖼𝗎𝗋𝖾:∀𝜉.(𝜉\𝗌𝖾𝖼𝗎𝗋𝖾)⇒𝖱𝖾𝖼(𝜉)→𝖱𝖾𝖼({𝗌𝖾𝖼𝗎𝗋𝖾:𝖡𝗈𝗈𝗅∣𝜉}),𝗐𝗂𝗍𝗁𝗈𝗎𝗍𝖯𝗈𝗋𝗍:∀𝛼𝜉.(𝜉\𝗉𝗈𝗋𝗍)⇒𝖱𝖾𝖼({𝗉𝗈𝗋𝗍:𝛼∣𝜉})→𝖱𝖾𝖼(𝜉). For example, put 𝑃𝑠={𝜉\𝗌𝖾𝖼𝗎𝗋𝖾}. Before generalization, the whole derivation of the second definition is 𝑟:𝖱𝖾𝖼(𝜉)⊢𝑞𝗍𝗋𝗎𝖾:𝑃𝑠⇒𝖡𝗈𝗈𝗅,𝑟:𝖱𝖾𝖼(𝜉)⊢𝑞𝑟:𝑃𝑠⇒𝖱𝖾𝖼(𝜉),𝗇𝖿(𝑃𝑠∪𝑃𝑠∪{𝜉\𝗌𝖾𝖼𝗎𝗋𝖾})=𝑃𝑠. Thus Q-Extend gives the premise of the following Q-Lam instance: 𝑟:𝖱𝖾𝖼(𝜉)⊢𝑞{𝗌𝖾𝖼𝗎𝗋𝖾=𝗍𝗋𝗎𝖾∣𝑟}:𝑃𝑠⇒𝖱𝖾𝖼({𝗌𝖾𝖼𝗎𝗋𝖾:𝖡𝗈𝗈𝗅∣𝜉})⋅⊢𝑞𝗐𝗂𝗍𝗁𝖲𝖾𝖼𝗎𝗋𝖾:𝑃𝑠⇒𝖱𝖾𝖼(𝜉)→𝖱𝖾𝖼({𝗌𝖾𝖼𝗎𝗋𝖾:𝖡𝗈𝗈𝗅∣𝜉})Q−Lam. The two leaves are predicate weakenings of Q-Const and Q-Var. The same two-step calculation for 𝗉𝗈𝗋𝗍𝖮𝖿 ends in Q-Select; for 𝗐𝗂𝗍𝗁𝗈𝗎𝗍𝖯𝗈𝗋𝗍 it ends in Q-Restrict.
For instance, instantiate 𝜉 in 𝗐𝗂𝗍𝗁𝖲𝖾𝖼𝗎𝗋𝖾 by {𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀,𝗉𝗈𝗋𝗍:𝖭𝖺𝗍}. The lacks premise reduces twice by L-Extend and ends at L-Empty; the result retains both fields and adds 𝗌𝖾𝖼𝗎𝗋𝖾.
The residual part of Q-Let is visible only when the environment fixes a row variable. Let Γ=𝑟:𝖱𝖾𝖼(𝜉),𝑃𝑠={𝜉\𝗌𝖾𝖼𝗎𝗋𝖾}. The extension calculation above gives Γ⊢𝑞{𝗌𝖾𝖼𝗎𝗋𝖾=𝗍𝗋𝗎𝖾∣𝑟}:𝑃𝑠⇒𝖱𝖾𝖼({𝗌𝖾𝖼𝗎𝗋𝖾:𝖡𝗈𝗈𝗅∣𝜉}). Here 𝜉∈ftv(Γ), so the generalized prefix is empty and 𝑃𝗋𝑠=𝑃𝑠. Even if the bound variable is unused, Γ⊢𝑞𝗅𝖾𝗍𝑥={𝗌𝖾𝖼𝗎𝗋𝖾=𝗍𝗋𝗎𝖾∣𝑟}𝗂𝗇𝗓𝖾𝗋𝗈:𝑃𝑠⇒𝖭𝖺𝗍. The constraint remains because it forms the bound expression; dropping it would accept an ambient record that already contains 𝗌𝖾𝖼𝗎𝗋𝖾. By contrast, in the closed definition 𝗐𝗂𝗍𝗁𝖲𝖾𝖼𝗎𝗋𝖾 the row variable is absent from the environment, so the same predicate is generalized with 𝜉.
The same instantiation fails on a row already containing 𝗌𝖾𝖼𝗎𝗋𝖾. That rejection is intentional: extension means adding a new field. Replacement is written with update, whose input explicitly contains the old field.
★☆☆ Derive the scheme of update from restriction and extension using the defining equation above. Write the single tail row at every intermediate type and identify the one lacks predicate used by both operations.
A record with row 𝜌 carries every field of 𝜌. A variant with the same row carries exactly one of them. The shared row language makes record extension and variant widening dual operations.
Extend the grammar by injection ⟨ℓ=𝑒⟩, embedding 𝖾𝗆𝖻𝖾𝖽ℓ𝑒, and the binding form 𝖼𝖺𝗌𝖾ℓ𝑒𝗈𝖿{⟨ℓ=𝑥⟩↦𝑒1;𝑦↦𝑒2}, where 𝑥 binds in 𝑒1 and 𝑦 in 𝑒2. Injection selects the new alternative, while embedding preserves a value from the old row.
In Q-Inject, the payload derivation determines 𝜏, while the typing derivation chooses the residual row 𝜌. In Q-Embed, the embedded value inhabits an old alternative, so it supplies no value at the newly added ℓ alternative. Fresh variables make both choices most general.
Injection chooses the new alternative. Embedding preserves a value from the old alternatives. Decomposition reverses that choice. The following calculation shows decomposition and embedding; injection is practiced in the following exercise.
Assume 𝑒:𝖵𝖺𝗋({𝗈𝗄:𝛼,𝖾𝗋𝗋𝗈𝗋:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜉}),𝜉\𝗈𝗄,𝜉\𝖾𝗋𝗋𝗈𝗋. First permute the row to expose 𝖾𝗋𝗋𝗈𝗋. With 𝜌={𝗈𝗄:𝛼∣𝜉}, the case term 𝖼𝖺𝗌𝖾𝖾𝗋𝗋𝗈𝗋𝑒𝗈𝖿{⟨𝖾𝗋𝗋𝗈𝗋=𝑠⟩↦𝗓𝖾𝗋𝗈;𝑦↦𝗓𝖾𝗋𝗈} has type 𝖭𝖺𝗍: under 𝑠:𝖲𝗍𝗋𝗂𝗇𝗀 the first body is 𝗓𝖾𝗋𝗈:𝖭𝖺𝗍, and under 𝑦:𝖵𝖺𝗋(𝜌) the second body has the same type. The second branch receives the whole residual variant; it may immediately decompose the known 𝗈𝗄 alternative. An alternative represented only by 𝜉 can be decomposed later only when its label and a corresponding lacks fact are known.
Writing 𝑃𝑒 for the two displayed lacks predicates, the complete rule instance is Γ⊢𝑞𝑒:𝑃𝑒⇒𝖵𝖺𝗋({𝖾𝗋𝗋𝗈𝗋:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜌})Γ,𝑠:𝖲𝗍𝗋𝗂𝗇𝗀⊢𝑞𝗓𝖾𝗋𝗈:∅⇒𝖭𝖺𝗍Γ,𝑦:𝖵𝖺𝗋(𝜌)⊢𝑞𝗓𝖾𝗋𝗈:∅⇒𝖭𝖺𝗍𝗇𝖿(𝑃𝑒∪∅∪∅)=𝑃𝑒Γ⊢𝑞𝖼𝖺𝗌𝖾𝖾𝗋𝗋𝗈𝗋𝑒𝗈𝖿{⟨𝖾𝗋𝗋𝗈𝗋=𝑠⟩↦𝗓𝖾𝗋𝗈;𝑦↦𝗓𝖾𝗋𝗈}:𝑃𝑒⇒𝖭𝖺𝗍Q−Case.
Conversely, if 𝑢:𝖵𝖺𝗋(𝜌), then 𝖾𝗆𝖻𝖾𝖽𝖾𝗋𝗋𝗈𝗋𝑢 has the original larger variant type. No case is lost and no run-time tag is fabricated.
★★☆ Let 𝑓:𝛼→𝛽 and 𝑒:𝖵𝖺𝗋({𝗈𝗄:𝛼,𝖾𝗋𝗋𝗈𝗋:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜉}) under the two corresponding lacks predicates. Construct a term of type 𝖵𝖺𝗋({𝗈𝗄:𝛽,𝖾𝗋𝗋𝗈𝗋:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜉}) which applies 𝑓 only to the 𝗈𝗄 alternative and preserves every other alternative. Display the decomposition, the new injection in the 𝗈𝗄 branch, and the embedding that puts the residual branch at the common result type.
The constraints now have an operational purpose. A record is represented by a finite label-to-value map; a variant by one labeled value. The source syntax does not prescribe an ordering in memory yet.
Evaluation is left-to-right call by value. Write 𝑣::=𝑐∣𝜆𝑥.𝑒∣𝑅∣𝑉,𝑅::={}∣{ℓ=𝑣∣𝑅},𝑉::=⟨ℓ=𝑣⟩,E::=[]∣E𝑒∣𝑣E∣𝗅𝖾𝗍𝑥=E𝗂𝗇𝑒∣{ℓ=E∣𝑒}∣{ℓ=𝑣∣E}∣E.ℓ∣E−ℓ∣⟨ℓ=E⟩∣𝖾𝗆𝖻𝖾𝖽ℓE∣𝖼𝖺𝗌𝖾ℓE𝗈𝖿{⟨ℓ=𝑥⟩↦𝑒1;𝑦↦𝑒2}. Record values 𝑅 have distinct labels. This is the complete evaluation context grammar for the term syntax developed so far. The two base contractions are (𝜆𝑥.𝑒)𝑣⟶𝑒[𝑣/𝑥],𝗅𝖾𝗍𝑥=𝑣𝗂𝗇𝑒⟶𝑒[𝑣/𝑥], and the compatible rule is E⟨𝑒⟩⟶E⟨𝑒′⟩ whenever 𝑒⟶𝑒′. Write 𝑒⟶∗𝑒′ for the reflexive–transitive closure of this source reduction. The primitive redexes are {ℓ=𝑣∣𝑅}.ℓ⟶𝑣,{𝑚=𝑤∣𝑅}.ℓ⟶𝑅.ℓ(𝑚≠ℓ),{ℓ=𝑣∣𝑅}−ℓ⟶𝑅,{𝑚=𝑤∣𝑅}−ℓ⟶{𝑚=𝑤∣𝑅−ℓ}(𝑚≠ℓ),𝖾𝗆𝖻𝖾𝖽ℓ⟨𝑚=𝑣⟩⟶⟨𝑚=𝑣⟩(𝑚≠ℓ),𝖼𝖺𝗌𝖾ℓ⟨ℓ=𝑣⟩𝗈𝖿{⟨ℓ=𝑥⟩↦𝑒1;𝑦↦𝑒2}⟶𝑒1[𝑣/𝑥],𝖼𝖺𝗌𝖾ℓ⟨𝑚=𝑣⟩𝗈𝖿{⟨ℓ=𝑥⟩↦𝑒1;𝑦↦𝑒2}⟶𝑒2[⟨𝑚=𝑣⟩/𝑦](𝑚≠ℓ). Record extension with an already present label and variant embedding of an ℓ-tag are not run-time states of a well-typed program; the lacks premise excludes them. Update first restricts and then extends.
For a neutral scrutinee 𝑧, a variant case does not reduce. Its typing derivation contains premises for both branch bodies, one under the inserted label and one under the residual row; neither premise determines which tag a future closing substitution will reveal.
Weakening and term substitution are unchanged by rows: predicates and types contain no term variables. Type-and-row substitution requires one extra observation.
Formation and equality. If 𝑃⊢𝜌𝗋𝗈𝗐, then 𝑄⊢𝜌[𝑆]𝗋𝗈𝗐; if 𝜌≡𝑃𝜌′, then 𝜌[𝑆]≡𝑄𝜌′[𝑆].
Type-and-row substitution. From Γ⊢𝑞𝑒:𝑃⇒𝜏 follows Γ[𝑆]⊢𝑞𝑒:𝑄⇒𝜏[𝑆].
Term substitution. If 𝑥 has monotype 𝜏𝑥, Γ,𝑥:𝜏𝑥⊢𝑞𝑒:𝑃⇒𝜏, and Γ⊢𝑞𝑎:𝑃𝑎⇒𝜏𝑥, then Γ⊢𝑞𝑒[𝑎/𝑥]:𝑄⇒𝜏 for 𝑄=𝗇𝖿(𝑃∪𝑃𝑎). If instead Γ,𝑥:GenΓ(𝑃𝑎⇒𝜏𝑎)⊢𝑞𝑒:𝑃⇒𝜏,Γ⊢𝑞𝑎:𝑃𝑎⇒𝜏𝑎, then Γ⊢𝑞𝑒[𝑎/𝑥]:𝑃⇒𝜏: the constraints of every instance of 𝑎 are entailed at the corresponding use of 𝑥.
Weakening. Weakening by a declaration fresh for the term preserves typing.
Proof. For item 1, normalize the substituted entailment derivation. An assumption 𝜉\ℓ becomes a predicate whose normal form occurs in 𝑄. Empty and unequal extension commute with 𝑆, because substitutions do not change labels. Item 2 is simultaneous induction on formation and equality. At Row-Ext, substitution sends the premise 𝑃⊩𝜌\ℓ to 𝑄⊩𝜌[𝑆]\ℓ; an adjacent exchange retains its two distinct labels.
For item 3, induct on qualified typing. Variable and constant instances compose their prefix substitution with 𝑆. Application and each record/variant rule normalize the substituted union of premise constraints. Lambda is structural. In Q-Let, write its first predicates as 𝑃1=𝑃𝗀1∪𝑃𝗋1 and freshen the generalized prefix away from 𝑆. Define the finite set without choosing formation derivations: 𝐹={𝜉\ℓ∈𝑄∣𝜉∈ftv(Γ[𝑆])}. Formation is syntax-directed on a row, so these are exactly the possible assumption leaves used to form the context images. Every variable of 𝐹 occurs in Γ[𝑆]. Then 𝑃𝑆1=𝗇𝖿(𝑃1[𝑆]∪𝐹) contains the normal forms of every substituted premise and every context assumption used by 𝑆. Hence the restriction of 𝑆 is an admissible map 𝑃1→𝑃𝑆1, so the first induction hypothesis gives Γ[𝑆]⊢𝑞𝑒1[𝑆]:𝑃𝑆1⇒𝜏1[𝑆]. The body induction hypothesis initially uses the declaration 𝑆⋅GenΓ(𝑃1⇒𝜏1). Apply lemma 4.12 to replace it by GenΓ[𝑆](𝑃𝑆1⇒𝜏1[𝑆]). Finally 𝑄 entails 𝐹 and 𝑃𝗋1[𝑆], so Q-Let returns exactly the ambient predicate context 𝑄. The case rule freshens both branch binders. Conversion uses item 2.
For item 4, induct on typing and freshen lambda, let, and case binders away from 𝑎. At a monomorphic distinguished variable leaf use the premise for 𝑎 and normalize the union. At a polymorphic let leaf, its symbolic instance is a substitution 𝑇 on the generalized variables. First use the leaf premises to regard 𝑇, extended by the identity elsewhere, as an admissible map 𝑃𝑎→𝑃: its images are 𝑃-formed and 𝑃 entails 𝗇𝖿(𝑃𝑎[𝑇]). Item 3 therefore types the same term 𝑎 at 𝜏𝑎[𝑇] directly under the surrounding 𝑃. The domain of 𝑇 contains only variables generalized away from Γ, so Γ[𝑇]=Γ. Thus no extra copy of the scheme’s predicates is added to the conclusion. Every other variable is unchanged. Each record, variant, and case rule reapplies after the induction hypotheses for its immediate subterms. Weakening is the same induction, because no old variable leaf selects the fresh declaration. ◻
Proof. Admissibility says that every image is formed and every predicate in 𝑃[𝐺] normalizes to ⊤, hence 𝗇𝖿(𝑃[𝐺])=∅. Now item 3 of lemma 4.18 applies. ◻
The word admissible cannot be replaced by ground. A raw map sending an unconstrained 𝜉 to {ℓ:𝖭𝖺𝗍,ℓ:𝖡𝗈𝗈𝗅} grounds 𝜉 and vacuously satisfies the empty predicate set, but its image is not a strict row and cannot classify a term.
The preservation proof needs to peel a run-time map in the same way as its static row.
Proof. Strip final uses of Q-Conv. Lemma 7.6(2) ensures that each conversion retains the outer 𝖱𝖾𝖼 constructor and projects to an equality of its rows. The only rule producing the displayed record constructor is Q-Extend, whose two premises give 𝑤 and 𝑅; its conclusion gives the extended row. If a premise uses a smaller normalized predicate context, apply lemma 7.12 to restore 𝑃, then reapply the stripped conversions to obtain the first equality and both displayed qualified typings. For item 2, use the finite-map description of lemma 4.5: both orders of deletion leave the same finite map and the same row-variable or empty tail, so they are equal under 𝑃. ◻
Let 𝑃 be normalized, let 𝑃⊢𝜌𝗋𝗈𝗐, and let Γ be a context of 𝑃-formed schemes.
If Γ⊢𝑞𝑅:𝑃⇒𝖱𝖾𝖼(𝜌) and 𝑅 is a record value, then either 𝑅={} and 𝜌≡𝑃𝜖𝜌, or 𝑅={ℓ=𝑤∣𝑅0} and there are 𝜏,𝜌0 such that 𝜌≡𝑃{ℓ:𝜏∣𝜌0},Γ⊢𝑞𝑤:𝑃⇒𝜏,Γ⊢𝑞𝑅0:𝑃⇒𝖱𝖾𝖼(𝜌0).
If Γ⊢𝑞𝑉:𝑃⇒𝖵𝖺𝗋(𝜌) and 𝑉 is a variant value, then 𝑉=⟨ℓ=𝑤⟩ and there are 𝜏,𝜌0 such that 𝜌≡𝑃{ℓ:𝜏∣𝜌0},Γ⊢𝑞𝑤:𝑃⇒𝜏.
If Γ⊢𝑞𝑣:𝑃⇒𝐴→𝐵 and 𝑣 is a value, then 𝑣=𝜆𝑥.𝑒. This uses the deliberately nullary constant signature Σ0; it contains no arrow-valued constants.
When 𝑃=∅, Γ=⋅, and 𝜌 is closed, repeated use of item 1 shows that a record value has exactly one value at each label of 𝜌; item 2 selects exactly one field of a closed variant row.
Proof. Strip final conversions and inspect the last introduction rule. Rules Q-Empty and Q-Extend give the two record cases and their displayed premises; Q-Inject gives the variant case. Projecting the stripped conversion through 𝖱𝖾𝖼 or 𝖵𝖺𝗋 supplies the row equalities by lemma 7.6(2). An embedding is not a value form. Base constants, records, and variants cannot have arrow type, so the remaining arrow-valued form is an abstraction.
In the closed record case, repeat item 1 down the finite value spine. Every Q-Extend premise proves that the new label is absent from the tail, so no label repeats. The terminal Q-Empty fixes the empty tail, and row equality preserves the resulting finite label-to-type map. The closed variant statement is item 2 together with the same finite-map characterization. ◻
Proof of Theorem 4.22 — Safety of strict records and variants
Proof. Ground discharge gives ⋅⊢𝑞𝑒:∅⇒𝜏[𝐺].
For preservation, prove first the stronger qualified assertion: if Γ⊢𝑞𝑎:𝑃0⇒𝜐 and 𝑎⟶𝑎′, then Γ⊢𝑞𝑎′:𝑃0⇒𝜐. Induct on the reduction, with the typing derivation as a subsidiary induction to remove a final conversion. Beta and let use term substitution. This stronger statement is important in the compatible rule for 𝗅𝖾𝗍𝑥=𝑎𝗂𝗇𝑏: the predicates stored in the scheme of 𝑥 need not occur among the body’s predicates, but the reduct of 𝑎 retains exactly its own qualified typing. For the let contraction, first weaken the body from 𝑃2 to 𝗇𝖿(𝑃𝗋1∪𝑃2) and then apply the generalized term-substitution clause (item 4) of lemma 4.18. The retained residual predicates keep the bound expression formed even when 𝑥 has no occurrence in the body.
For record lookup, perform an inner induction on the run-time record. At a matching head, lemma 4.20 proves that its stored type is the unique ℓ field type. At an unequal head 𝑚, peeling types the tail at 𝜌−𝑚; deletion commutation says this residual row still exposes ℓ at the same type, so the inner induction applies. Restriction uses the same inner induction, but reattaches each unequal 𝑚 field; deletion commutation proves that the resulting row is 𝜌−ℓ. At the matching head, the preservation subcalculation is therefore explicit: Γ⊢𝑞{ℓ=𝑣∣𝑟}:𝑃⇒𝖱𝖾𝖼({ℓ:𝜏∣𝜌})Γ⊢𝑞{ℓ=𝑣∣𝑟}.ℓ:𝑃⇒𝜏Q−Select{ℓ=𝑣∣𝑟}.ℓ𝑙𝑜𝑜𝑘𝑢𝑝⟶𝑣, and peeling the record derivation recovers Γ⊢𝑞𝑣:𝑃⇒𝜏, the type of the reduct. The unequal-head induction repeats this calculation on 𝑟.
For embedding, qualified value inversion says the old value is ⟨𝑚=𝑣⟩ with 𝑚 in the residual row. The lacks premise gives 𝑚≠ℓ, and injection at the enlarged row types the reduct. In the matching case clause, the payload has the field type 𝜏 from Q-Case; in the unequal clause, deleting ℓ from the finite map leaves the tag 𝑚 in 𝜌, so the residual value has type 𝖵𝖺𝗋(𝜌). More explicitly, lemma 4.5(3) writes the original row as {𝑚:𝜐∣𝜌−𝑚}; deletion of the distinct ℓ preserves that field, so Q-Inject types the residual tag. Term substitution types the selected branch. Compatible-context cases use the induction hypothesis under the corresponding displayed typing rule.
For progress, induct on the closed typing derivation with the strengthened property: for every predicate context 𝑃0 and every ground admissible 𝐻:𝑃0→∅, a closed judgment ⋅⊢𝑞𝑎:𝑃0⇒𝐴 has a subject that is a value or steps. Reduction does not inspect types or 𝐻, so changing the ground satisfier between premise calls does not change the subject or its next step. Every normalized predicate set has a ground admissible substitution: send every row variable to 𝜖𝜌 and every unconstrained type variable to 𝖭𝖺𝗍. Use such a substitution whenever an immediate premise carries predicates not retained by its conclusion. In particular, for a let, apply the induction hypothesis to the bound expression under a ground satisfier of its own 𝑃1; if it steps, use the let context, and if it is a value, use the let contraction. For application, first step the function; when it is a lambda value, step the argument; when both are values, beta reduction applies and term substitution types the reduct. Constants and lambdas are already values. For Q-Let, use the bound premise under its own normalized 𝑃1, as above, because its generalized predicates need not occur in the conclusion. In every other multi-premise rule, each premise constraint belongs to the conclusion’s normalized union, so one ground satisfier serves all immediate premises.
If the scrutinee of a row operation steps, use the compatible rule. If it is a value, lemma 4.21 proves that it has the required field form, residual-record form, or one of the two variant forms, so exactly one displayed primitive clause applies. The strictness premise rules out the two forbidden duplicate states named after definition 4.17. ◻
Proof of Lemma 7.28 — Determinism of the extended source
Proof. Every nonvalue that steps has a unique decomposition 𝑒=E⟨𝑟⟩, where 𝑟 is one of the root redexes in definition 4.17. Prove this by induction on 𝑒, following the left-to-right context grammar. For example, a record selection first follows its unique scrutinee decomposition. Once its scrutinee is a record value, exactly one of 𝑚=ℓ and 𝑚≠ℓ selects the matching-head or unequal-head root clause. Restriction and variant case have the same exclusive split; an embedding root exists exactly when its tag differs from the inserted label. The beta and let roots have outer shapes distinct from all row roots. Hence two reductions of 𝑒 use the same context and root clause, whose contractum is unique. ◻
★★☆ Suppose 𝑚≠ℓ and a closed record value begins {𝑚=𝑤∣𝑅}. Write the full preservation step from {𝑚=𝑤∣𝑅}.ℓ to 𝑅.ℓ: expose the static 𝑚 field, type 𝑅, and use deletion commutation to show that its row still contains the same ℓ field.
Type safety says why lacks predicates are not bureaucratic decoration: they are the static form of the fact that the operational lookup and deletion recursions will find exactly one component.
Unifying rows
Inference must solve equations such as {𝗉𝗈𝗋𝗍:𝛼∣𝜉}≐{𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀,𝗉𝗈𝗋𝗍:𝖭𝖺𝗍∣𝜁}. The first labels disagree syntactically, but the equation is solvable: move 𝗉𝗈𝗋𝗍 to the front on the right, unify 𝛼 with 𝖭𝖺𝗍, and make the two remainders agree. Ordinary first-order decomposition cannot perform the first step. The operation it lacks is: expose a requested field wherever it occurs, extending an open tail if necessary.
Exposing a field in an open tail must also preserve strictness. A substitution-only insertion clause would return 𝐼0=[{ℓ:𝜏∣𝜁}/𝜉]withnoobligationon𝜁. Instantiating the supposedly fresh tail by 𝜁={ℓ:𝜐} then produces {ℓ:𝜏,ℓ:𝜐}, which is not a strict row. If the input context contains 𝜉\ℓ, applying 𝐼0 should instead produce ⊥, but a substitution-only result has nowhere to record that failure. The repair is to return the new lacks obligation together with the substitution. Replacing an open tail 𝜉 by {ℓ:𝜏∣𝜁} is valid only if the fresh tail 𝜁 lacks ℓ. Therefore insertion cannot return a substitution alone; it must return the new obligation at the moment the new tail is created.
The insertion and unification algorithms below are mutually recursive. Within their clauses, 𝑞,𝑟,𝜌,𝜌′ range over rows. The disambiguation table following definition 4.1 fixes the overloaded row operations; the two signatures displayed below fix the inputs and outputs of 𝗂𝗇𝗌𝖾𝗋𝗍 and 𝗌𝗈𝗅𝗏𝖾.
Given 𝑃⊢𝜏𝗍𝗒𝗉𝖾 and 𝑃⊢𝜌𝗋𝗈𝗐, 𝗂𝗇𝗌𝖾𝗋𝗍𝑃(ℓ:𝜏,𝜌)=(𝐼,𝑄,𝜌−) returns an admissible substitution 𝐼:𝑃→𝑄 and a residual row such that 𝜌[𝐼]=𝗋{ℓ:𝜏[𝐼]∣𝜌−},𝑄⊩𝜌−\ℓ. For one top-level call, let 𝐴 contain every variable in its predicate set, types, rows, equations, and any protected set named by the caller. Recursive calls share one supply; a fresh choice is the first variable, in the fixed type- and row-variable orders specified with generalization, outside 𝐴 and all earlier choices of that call. The insertion clauses are:
For a row variable 𝜉, provided 𝜉∉ftv(𝜏), choose fresh 𝜁, set 𝐼=[{ℓ:𝜏∣𝜁}/𝜉],𝑄=𝗇𝖿(𝑃[𝐼]∪{𝜁\ℓ}),𝜌−=𝜁. Fail if the occurs check or normalization fails.
Insertion into 𝜖𝜌 fails.
For 𝜌={ℓ:𝜐∣𝜌0}, compute 𝗌𝗈𝗅𝗏𝖾(𝑃;𝜏≐𝜐)=(𝑈,𝑄) and return (𝑈,𝑄,𝜌0[𝑈]).
For 𝜌={𝑚:𝜐∣𝜌0} with 𝑚≠ℓ, compute 𝗂𝗇𝗌𝖾𝗋𝗍𝑃(ℓ:𝜏,𝜌0)=(𝐼,𝑄,𝑞) and return (𝐼,𝑄,{𝑚:𝜐[𝐼]∣𝑞}).
The solver 𝗌𝗈𝗅𝗏𝖾(𝑃;𝐸)=(𝑈,𝑄) takes a finite predicate set 𝑃 and a finite, well-sorted list 𝐸 of equations between types or rows formed under 𝗇𝖿(𝑃). Every call first normalizes 𝑃 and fails if normalization reaches ⊥. The clauses below are tried in their printed order, so the algorithm is deterministic up to fresh names.
On the empty work list, return (id,𝗇𝖿(𝑃)).
Discard a syntactic identity 𝑎≐𝑎 at the head of a nonempty list.
If the head is 𝑧≐𝑎, where 𝑧 is a type variable and 𝑎 a type, or 𝑧 is a row variable and 𝑎 a row, fail when 𝑧∈ftv(𝑎). Otherwise put 𝐼=[𝑎/𝑧], normalize 𝑃1=𝗇𝖿(𝑃[𝐼]), solve 𝗌𝗈𝗅𝗏𝖾(𝑃1;𝐸[𝐼])=(𝑉,𝑄), and return (𝐼;𝑉,𝑄). If both sides are variables of the same sort, this clause eliminates the left variable. A head 𝑎≐𝑧 is oriented to 𝑧≐𝑎 only when 𝑎 is not itself a variable. Thus the orientation is deterministic, row-variable equations are eliminated explicitly, and both the old predicates and the remaining equations are rewritten by 𝐼. Thus U-Var fuses the orient and eliminate behaviours that definition 3.22 names separately.
This clause applies only to equations of type sort. Equal base-type constructors are discarded; unequal base types and unlike outer type constructors fail. Replace 𝜏1→𝜏2≐𝜐1→𝜐2 by the two equations 𝜏1≐𝜐1,𝜏2≐𝜐2 in that order. Replace 𝖱𝖾𝖼(𝜌)≐𝖱𝖾𝖼(𝑞) and 𝖵𝖺𝗋(𝜌)≐𝖵𝖺𝗋(𝑞) by 𝜌≐𝑞. A record constructor never unifies with a variant constructor.
Discard 𝜖𝜌≐𝜖𝜌. An empty row against an extension fails.
After the preceding variable clauses, an extension equation has an extension on each side. For {ℓ:𝜏∣𝜌}≐𝑞, compute 𝗂𝗇𝗌𝖾𝗋𝗍𝑃(ℓ:𝜏,𝑞)=(𝐼,𝑃1,𝑞−)𝗌𝗈𝗅𝗏𝖾(𝑃1;(𝜌[𝐼]≐𝑞−,𝐸[𝐼]))=(𝑉,𝑄)𝗌𝗈𝗅𝗏𝖾(𝑃;({ℓ:𝜏∣𝜌}≐𝑞,𝐸))=(𝐼;𝑉,𝑄). Here (𝑎≐𝑏,𝐸) means that 𝑎≐𝑏 is placed at the front of the remaining work list. Insertion may scan through several distinct labels before it reaches the requested one or an open tail.
These clauses, together with the four insertion clauses above, are the complete mutually recursive algorithm. Rigid decomposition always prepends its subequations from left to right.
For reference, the complete clause sheet is 𝗂𝗇𝗌𝖾𝗋𝗍1𝐼−𝑉𝑎𝑟𝜉2𝐼−𝐸𝑚𝑝𝑡𝑦𝜖𝜌(failure)3𝐼−𝑀𝑎𝑡𝑐ℎ{ℓ:𝜐∣𝜌0}4𝐼−𝑆𝑘𝑖𝑝{𝑚:𝜐∣𝜌0},𝑚≠ℓ𝗌𝗈𝗅𝗏𝖾1𝑈−𝐷𝑜𝑛𝑒[]2𝑈−𝐷𝑒𝑙𝑒𝑡𝑒syntacticidentity3𝑈−𝑉𝑎𝑟𝑧≐𝑎oritsallowedorientation4𝑈−𝑇𝑦𝑝𝑒type-sortrigidequation5𝑈−𝐸𝑚𝑝𝑡𝑦empty-rowcomparison6𝑈−𝑅𝑜𝑤extensionequationvia𝗂𝗇𝗌𝖾𝗋𝗍 These named rows list the ten clauses in priority order; the definitions above give their outputs and failure conditions. The algorithm is a partial recursive function, so these are defining clauses rather than derivability rules.
The first insertion clause is the point that a “unify first, check lacks later” presentation misses. If 𝑃 already contains 𝜉\𝑚 with 𝑚≠ℓ, substitution reduces it to 𝜁\𝑚. If it contains 𝜉\ℓ, normalization reaches ⊥, correctly rejecting an attempt to expose a field known to be absent. Independently, 𝜁\ℓ makes the newly constructed row itself strict.
For the equation displayed at the start of the section, insertion scans past 𝗁𝗈𝗌𝗍, meets 𝗉𝗈𝗋𝗍:𝖭𝖺𝗍, and unifies 𝛼 with 𝖭𝖺𝗍. It returns the residual row {𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜁}, so the remaining equation is 𝜉≐{𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜁}. No new lacks predicate is needed in this particular scan: both fields were already displayed in a strict input row.
The fresh-tail clause appears in the smallest crossing equation. Start with 𝑃={𝜉\𝗑,𝜁\𝗒} and solve {𝗑:𝖭𝖺𝗍∣𝜉}≐{𝗒:𝖡𝗈𝗈𝗅∣𝜁}. To expose 𝗑 on the right, insertion first passes the distinct 𝗒 field and then reaches the open tail 𝜁. Choose fresh 𝜔 and set 𝜁[𝐼]={𝗑:𝖭𝖺𝗍∣𝜔}. The new tail must lack 𝗑. The old predicate 𝜁\𝗒 also becomes 𝜔\𝗒. After reattaching the stored 𝗒 field, the residual equation is 𝜉≐{𝗒:𝖡𝗈𝗈𝗅∣𝜔}. Eliminating 𝜉 leaves the principal answer 𝑈=[{𝗑:𝖭𝖺𝗍∣𝜔}/𝜁,{𝗒:𝖡𝗈𝗈𝗅∣𝜔}/𝜉],𝑄={𝜔\𝗑,𝜔\𝗒}. Thus the fresh tail is not an implementation trick: its two lacks facts are exactly what makes both substituted rows strict. Any larger common tail is obtained by an admissible instance of 𝜔.
For finite multisets of natural numbers, write 𝑀<mul𝑁 when there are a nonempty submultiset 𝑋⊆𝑁 and a finite multiset 𝑌 such that 𝑀=(𝑁−𝑋)⊎𝑌,andforevery𝑦∈𝑌some𝑥∈𝑋satisfies𝑦<𝑥. Thus one or more entries are removed and may be replaced by finitely many strictly smaller entries.
Proof of Lemma 7.32 — Well-founded multiset descent
Proof. Suppose an infinite descending chain 𝑀0>mul𝑀1>mul⋯ existed. A descending step never creates an entry larger than one it removes, so the maximum entry of 𝑀0 bounds every later multiset. For multisets whose entries are at most 𝑘, induct on 𝑘. At 𝑘=0, a descending step can only delete zeroes, so cardinality decreases. For the successor step, the number of occurrences of 𝑘 never increases; whenever a removed occurrence of 𝑘 is not restored, that number decreases. Along the assumed infinite chain this can happen only finitely often, so the count is eventually constant. Remove those fixed occurrences of 𝑘 from every remaining multiset. Every subsequent change then concerns entries below 𝑘, where the induction hypothesis excludes an infinite descending chain. ◻
Proof. Let 𝐿 be the finite set of labels occurring in the initial predicate set and equation list. No clause invents a label. At any recursive call, apply the accumulated substitution and regard each remaining row-variable tail 𝑡 as carrying the set 𝐹𝑡:={ℓ∈𝐿∣thenormalizedpredicatecontextcontains𝑡\ℓ}. Equation size alone is not a decreasing measure. In I-Var, replacing 𝜉 by {ℓ:𝜏∣𝜁} can make a residual equation strictly larger: 𝜉≐𝜌isreplacedby{ℓ:𝜏∣𝜁}≐𝜌. The decreasing resource is instead the set of labels that each open tail is still permitted to absorb. Define Φ:=∑𝑡(|𝐿|−|𝐹𝑡|). Here 𝑡 ranges over the distinct row variables in the normalized active input and predicate context, not over their occurrences. The sum is finite because those objects are finite. A global substitution may duplicate occurrences of a tail, but it cannot duplicate a summand.
When insertion exposes ℓ at a variable tail 𝑡, normalization fails if ℓ∈𝐹𝑡. Otherwise the clause replaces 𝑡 globally by one fresh tail 𝑡′ and gives 𝐹𝑡′=𝐹𝑡∪{ℓ}; hence Φ decreases by exactly one. If variable elimination identifies two tails, their forbidden sets are unioned and one tail disappears. This cannot increase Φ: for 𝐹,𝐺⊆𝐿, |𝐿|−|𝐹∪𝐺|≤(|𝐿|−|𝐹|)+(|𝐿|−|𝐺|). Eliminating a tail against a closed row removes its summand. Eliminating it against a displayed row with one existing tail has the same union calculation after the displayed labels have been checked. Thus every row-variable elimination leaves Φ unchanged or decreases it, and fresh-tail creation decreases it strictly.
Let the size of a type or row be its number of syntax nodes, and put |𝑎≐𝑏|=1+|𝑎|+|𝑏|,|(ℓ:𝜏,𝜌)|=1+|𝜏|+|𝜌|. At a solver call, let 𝑀 be the multiset of equation sizes; at an insertion call, let it be the singleton containing the insertion-input size. Let 𝑁 count the distinct type variables and row tails in the normalized predicates and active input, after applying the accumulated substitution. Use the lexicographic measure (Φ,𝑁,𝑀), where 𝑀 is ordered by <mul.
Every mutually recursive edge is recorded in the following table. A dash means that the earlier component is merely nonincreasing; the rightmost named component is the first strict decrease.
caller clause
callee clause
Φ
𝑁
strict component and reason
U-Var
solve
–
↓
𝑁: occurs-safe elimination
U-Delete/U-Empty
solve
–
–
𝑀: delete one equation
U-Type
solve
–
–
𝑀: proper subterms
U-Row
insert
–
–
𝑀: smaller insertion input
I-Match
solve
–
–
𝑀: field-type subterms
I-Skip
insert
–
–
𝑀: proper row tail
U-Row after insert
solve
–
–
first of Φ,𝑁,𝑀 changed below
For U-Var, the occurs check prevents the eliminated variable from reappearing. U-Delete, U-Type, and U-Empty delete an equation or replace one rigid equation by equations between proper subexpressions. In definition 7.31, take 𝑋 to contain the removed equation size and 𝑌 to contain the sizes of the proper-subexpression equations; deletion takes 𝑌=∅. Thus 𝑀 decreases when the earlier components are unchanged. The first call of U-Row, from solving to insertion, replaces its extension equation by the strictly smaller input (ℓ:𝜏,𝑞). I-Match replaces an insertion input by the strictly smaller equation 𝜏≐𝜐, while I-Skip replaces it by the input on a proper row tail. Each of these is the single-element instance 𝑋={𝑛} and 𝑌={𝑛′} with 𝑛′<𝑛.
It remains to compare the second call of U-Row, which solves the residual row equation and old work list. If its insertion computation uses I-Var at any depth, the new lacks fact makes Φ strictly smaller. Otherwise insertion creates no tail. If its returned substitution is nonidentity, some nested solver call used U-Var on a type or row variable; no other clause creates a binding when I-Var is absent. The occurs check prevents that eliminated variable from reappearing, so 𝑁 strictly decreases. If the returned substitution is the identity, the old work list is unchanged and the residual comparison has removed the matched displayed field, so 𝑀 strictly decreases. These cases cover every actual mutually recursive edge. Lemma 7.32 and the lexicographic composition of the well-founded orders on ℕ,ℕ, and <mul therefore prove termination. ◻
Let 𝑍 be a finite set of variables disjoint from the active inputs of an insertion or solver call. The call may choose every fresh tail outside 𝑍, without changing its result except for fresh renaming, and its returned substitution is the identity on 𝑍.
Proof of Lemma 4.26 — Fresh support of insertion and solving
Proof. Induct over the mutually recursive clauses. Empty, rigid, and failure clauses introduce no substitution. A variable clause changes only the displayed input variable; an exposing insertion changes only that input variable and chooses its new tail outside 𝑍. Composition preserves identity on 𝑍. In a recursive clause pass 𝑍 together with all variables of the surrounding arguments that are absent from the recursive call. Thus neither the recursive result nor a later fresh choice can acquire one of those names. This proves both assertions simultaneously. ◻
Insertion. Suppose insertion returns 𝗂𝗇𝗌𝖾𝗋𝗍𝑃(ℓ:𝜏,𝜌)=(𝐼,𝑄,𝜌−). Then equation 4.1 holds and 𝑄⊢𝜌−𝗋𝗈𝗐,𝜌[𝐼]≡𝑄{ℓ:𝜏[𝐼]∣𝜌−}. If 𝑆:𝑃→𝑃′ is admissible and 𝜌[𝑆]≡𝑃′{ℓ:𝜏[𝑆]∣𝑞}, then 𝑆≡𝑃′𝐼;𝑅 on the variables of 𝑃,𝜏,𝜌 for some admissible 𝑅:𝑄→𝑃′. If insertion fails, no such 𝑆 exists.
Solving. Suppose 𝐸 is well sorted and formed under 𝑃. If 𝗌𝗈𝗅𝗏𝖾(𝑃;𝐸)=(𝑈,𝑄), then 𝑈:𝑃→𝑄 is admissible and solves every equation of 𝐸 under 𝑄. Every admissible solution 𝑆:𝑃→𝑃′ factors as 𝑆≡𝑃′𝑈;𝑅 on the variables of 𝑃,𝐸 for some admissible 𝑅:𝑄→𝑃′. If the solver fails, no admissible solution exists. In particular, a ground admissible solution of 𝐸 factors through 𝑈 by a ground admissible map out of 𝑄.
Proof. The calculation terminates by lemma 4.25. Use well-founded induction on that calculation, proving the insertion and solver clauses simultaneously.
For variables in a recursive input, the induction hypothesis gives 𝑆≡𝑃′𝐼;𝑅. The remaining variables form a protected set 𝑍. For each 𝑧∈𝑍, define 𝑅(𝑧)=𝑆(𝑧); fresh support gives 𝐼(𝑧)=𝑧, so the same factorization equation holds on the surrounding input without changing an equation already factored.
For insertion into a row variable 𝜉, a target solution has, by lemma 4.5, a unique decomposition 𝜉[𝑆]≡𝑃′{ℓ:𝜏[𝑆]∣𝑞} with 𝑃′⊩𝑞\ℓ. Define 𝜁[𝑅]=𝑞 and let 𝑅 agree with 𝑆 on the old variables. The displayed lacks judgment satisfies the new predicate 𝜁\ℓ; admissibility of 𝑆 satisfies every predicate inherited from 𝑃[𝐼]. Hence 𝑅:𝑄→𝑃′ and 𝐼;𝑅≡𝑃′𝑆 on the insertion variables. Regard types and rows as one finite, mutually defined syntax tree: arrow components, record/variant indices, and extension field types and tails are its children. If the occurs check finds 𝑧∈ftv(𝑎), any solution would equate the finite tree 𝑆(𝑧) with a tree containing 𝑆(𝑧) as a proper descendant, which is impossible. The empty row cannot expose a field. If normalization of 𝑃[𝐼]∪{𝜁\ℓ} fails, an inherited predicate entails 𝜉\ℓ. Substitution stability would make every admissible target 𝑆:𝑃→𝑃′ satisfy 𝑃′⊩𝜉[𝑆]\ℓ, while the required insertion equality writes 𝜉[𝑆] as {ℓ:𝜏[𝑆]∣𝑞}. A formed strict row with an ℓ-field cannot lack ℓ, so no target solution exists.
Suppose the insertion input begins with ℓ:𝜐. Strict finite-map equality separates the target equation into 𝜏[𝑆]≡𝑃′𝜐[𝑆] and 𝜌0[𝑆]≡𝑃′𝑞. Apply the simultaneous induction hypothesis to the smaller type-equation solve, protecting 𝑍=ftv(𝜌0)∖ftv(𝑃,𝜏,𝜐). Its factor 𝑅0 agrees with 𝑆 after 𝑈 on the active solver variables. By lemma 4.26, 𝑈 fixes 𝑍. Extend 𝑅0 by 𝑧[𝑅]=𝑧[𝑆] for 𝑧∈𝑍. The new images are formed because 𝑆 is admissible, while no predicate of the solver output acquires a protected variable; hence the extension remains admissible. It now satisfies 𝜌0[𝑈;𝑅]≡𝑃′𝜌0[𝑆]≡𝑃′𝑞. Meanwhile substitution stability carries the original tail-lacks premise to 𝑄⊩𝜌0[𝑈]\ℓ. This proves the returned specification and its factorization.
Now let the insertion input be {𝑚:𝜐∣𝜌0} with 𝑚≠ℓ. In any target solution the unique 𝑚 field occurs in 𝑞. Delete it and put 𝑞𝑚=𝑞−𝑚. Deleting 𝑚 from the target equality gives 𝜌0[𝑆]≡𝑃′{ℓ:𝜏[𝑆]∣𝑞𝑚}. Protect also 𝑍=ftv(𝜐)∖ftv(𝑃,𝜏,𝜌0). The insertion induction hypothesis on the proper tail yields a factor 𝑅0 and residual 𝑟 such that 𝑅0 agrees with 𝑆 after 𝐼 on the recursive input. By lemma 4.26, 𝐼 fixes 𝑍; extend 𝑅0 by 𝑧[𝑅]=𝑧[𝑆] there, preserving admissibility as in the same-head case. Then reattaching the stored 𝑚:𝜐[𝐼] gives the algorithm’s residual {𝑚:𝜐[𝐼]∣𝑟}, and applying 𝑅 makes it 𝑃′-equal to 𝑞. The original strict tail lacks 𝑚, and deletion of the distinct ℓ field preserves that fact, so 𝑟 lacks 𝑚. It also lacks ℓ by the induction hypothesis. Thus the reattached residual is formed and the adjacent exchange with ℓ proves the insertion specification.
For the solver, the ordinary sorted variable and rigid-constructor cases are the elimination and decomposition calculations of theorem 3.26, performed separately for type variables and row variables. Each recursive result must additionally normalize its target predicate context; admissibility of the elementary substitution supplies the formation and entailment premises for that normalization. In a variable elimination, the equation’s formation premise proves that the image is formed, the occurs check proves that the eliminated variable is absent from that image, and normalization forms the target predicate context. Therefore lemma 7.16 proves that the elementary substitution is admissible. In particular, a row image remains a formed strict row, and the same substitution preserves formation of the remaining equations by the direct formation induction used in that lemma. Then lemma 4.11 composes the returned factors. In the row-extension case, protect 𝑍=ftv(𝜌,𝐸)∖ftv(𝑃,𝜏,𝑞) in the insertion call. Restricting an arbitrary target solution to the insertion equation gives a solution of that insertion problem. Its induction hypothesis gives a factor 𝑅1 on the active insertion variables. Extend 𝑅1 by the target solution on 𝑍; lemma 4.26 says 𝐼 fixes 𝑍, so the extended map is admissible and agrees with the target after 𝐼 on 𝜌,𝐸 as well. Because the target solution satisfies the original extension equation, strict finite-map uniqueness identifies its exposed ℓ-field with the one produced by insertion. Deleting that field on both sides gives the residual equation after 𝑅1; agreement on the protected variables also preserves every equation in the old work list. Thus 𝑅1 solves the residual equation and remaining work list. The solver induction hypothesis factors 𝑅1 through 𝑉, hence the target is 𝑃′-equivalent to the composite 𝐼;𝑉;𝑅2 on every original problem variable. Conversely, the insertion equation reattaches the same ℓ:𝜏[𝐼;𝑉] field to the residual rows equated by 𝑉, so 𝐼;𝑉 solves the original equation.
The failure cases use the same local obstructions. Predicate normalization at bottom contradicts admissibility. The occurs check contradicts finiteness of the joint tree. Unequal base or outer constructors contradict lemma 7.6(2). An empty row cannot equal an extension by lemma 4.5, and I-Empty is the same fact for insertion. The equal-head and unequal-head insertion failures reduce to a nested solver failure or to the duplicate-label contradiction displayed at the end of the I-Var paragraph. Hence each declared failure excludes a solution.
Finally suppose the competing solution 𝑆:𝑃→∅ is ground. In the constructions above, every old or protected variable is mapped as 𝑆 maps it, and every fresh tail is mapped to a residual row obtained by deleting fields from a closed 𝑆-image. Induction on the calculation therefore makes every image of the factor 𝑅:𝑄→∅ closed. This proves the ground conclusion; 𝑃′=∅ alone would not suffice. ◻
Take the initial formation context 𝑃0={𝜉\𝗉𝗈𝗋𝗍,𝜁\𝗁𝗈𝗌𝗍,𝜁\𝗉𝗈𝗋𝗍}. For 𝐸={{𝗉𝗈𝗋𝗍:𝛼∣𝜉}≐{𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀,𝗉𝗈𝗋𝗍:𝖭𝖺𝗍∣𝜁}}, insertion exposes 𝗉𝗈𝗋𝗍 on the right, unifies 𝛼 with 𝖭𝖺𝗍, and leaves 𝜉≐{𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜁}. Hence 𝑈=[𝖭𝖺𝗍/𝛼,{𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜁}/𝜉]. After applying 𝑈, the residual context is 𝑄={𝜁\𝗁𝗈𝗌𝗍,𝜁\𝗉𝗈𝗋𝗍}. The first predicate makes the substitution for 𝜉 a strict row; the second preserves both original row-formation derivations. Every other admissible solution chooses a row for 𝜁 satisfying 𝑄 and factors through 𝑈.
Now instead begin with 𝑃=𝑃0∪{𝜉\𝗁𝗈𝗌𝗍}. Applying 𝑈 gives {𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜁}\𝗁𝗈𝗌𝗍⇓⊥. The raw equations still unify, but the constrained problem has no admissible solution. This is the static detection of a duplicate-label extension.
★★☆ Run insertion and row unification on {𝗑:𝛼,𝗒:𝖡𝗈𝗈𝗅∣𝜉}≐{𝗓:𝖲𝗍𝗋𝗂𝗇𝗀,𝗑:𝖭𝖺𝗍∣𝜁}. First write the four residual lacks predicates needed to form both input rows. Give the MGU and its fresh common tail. Then also normalize the predicates 𝜉\𝗓 and 𝜁\𝗒. State whether the constrained problem succeeds.
Qualified Algorithm W returns principal source typings. Its predicates are carried through sequential calls and generalized with their types. Each saturated row form adds one syntax-directed clause using the principal solver. The application order is forced by the case term 𝖼𝖺𝗌𝖾ℓ⟨ℓ=𝗓𝖾𝗋𝗈⟩𝗈𝖿{⟨ℓ=𝑥⟩↦(𝜆𝑧.𝑧)𝑥;𝑦↦(𝜆𝑧.𝑧)𝗓𝖾𝗋𝗈}. The scrutinee first determines the selected payload type; that substitution must reach the matching branch before the residual branch is inferred, and both branch substitutions must reach the final result equation. The clauses below are the least left-to-right threading that makes those four obligations simultaneously well formed. The calculation immediately after the definition checks every stage on this term.
The judgmental notation 𝖶𝑟(Γ,𝑒)=(𝑃,𝑆,𝜏) means that inference returns predicates 𝑃, substitution 𝑆, and monotype 𝜏. At a top-level call, let 𝐴 be the finite set of all type and row variables, free or bound, written in Γ and the constant schemes used by 𝑒. The call owns two supplies in the fixed variable orders used by generalization. Each recursive call receives the unused suffixes; “choose fresh” means take the first variable outside 𝐴 and all earlier choices of this top-level call. The clauses are as follows.
For 𝑥 with Γ(𝑥)=∀¯𝛼¯𝜉.𝑄⇒𝜏, replace the prefix by fresh variables and return the fresh instance of (𝑄,id,𝜏).
For 𝜆𝑥.𝑒, choose fresh 𝛼, infer (𝑃,𝑆,𝜏) under Γ,𝑥:𝛼, and return (𝑃,𝑆,𝛼[𝑆]→𝜏).
Constants use the same fresh qualified instantiation as variables.
For 𝑒1𝑒2, infer (𝑃1,𝑆1,𝜏1), then (𝑃2,𝑆2,𝜏2) under Γ[𝑆1]. Choose fresh 𝛽, compute 𝗌𝗈𝗅𝗏𝖾(𝑃1[𝑆2]∪𝑃2;𝜏1[𝑆2]≐𝜏2→𝛽)=(𝑈,𝑄), and return (𝑄,𝑆1;𝑆2;𝑈,𝛽[𝑈]).
For 𝗅𝖾𝗍𝑥=𝑒1𝗂𝗇𝑒2, infer (𝑃1,𝑆1,𝜏1), put 𝑋1=ftv(𝑃1,𝜏1)∖ftv(Γ[𝑆1]),𝑃𝗀1={𝑝∈𝑃1∣ftv(𝑝)⊆𝑋1},𝑃𝗋1=𝑃1∖𝑃𝗀1,𝜎=GenΓ[𝑆1](𝑃1⇒𝜏1), infer (𝑃2,𝑆2,𝜏2) under Γ[𝑆1],𝑥:𝜎, and return (𝗇𝖿(𝑃𝗋1[𝑆2]∪𝑃2),𝑆1;𝑆2,𝜏2), failing if normalization does.
𝖶𝑟(Γ,{}) returns (∅,id,𝖱𝖾𝖼(𝜖𝜌)).
For {ℓ=𝑎∣𝑟}, infer (𝑃1,𝑆1,𝜏1) for 𝑎, then (𝑃2,𝑆2,𝜏2) for 𝑟 under Γ[𝑆1]. Choose fresh 𝜉 and solve 𝗌𝗈𝗅𝗏𝖾(𝑃1[𝑆2]∪𝑃2∪{𝜉\ℓ};𝜏2≐𝖱𝖾𝖼(𝜉))=(𝑈,𝑄). Return (𝑄,𝑆1;𝑆2;𝑈,𝖱𝖾𝖼({ℓ:𝜏1[𝑆2;𝑈]∣𝜉[𝑈]})).
For 𝑟.ℓ, infer (𝑃,𝑆,𝜏), choose fresh 𝛼,𝜉, solve 𝗌𝗈𝗅𝗏𝖾(𝑃∪{𝜉\ℓ};𝜏≐𝖱𝖾𝖼({ℓ:𝛼∣𝜉}))=(𝑈,𝑄), and return (𝑄,𝑆;𝑈,𝛼[𝑈]). Restriction uses the same calculation and returns (𝑄,𝑆;𝑈,𝖱𝖾𝖼(𝜉[𝑈])).
For ⟨ℓ=𝑎⟩, infer (𝑃,𝑆,𝜏), choose fresh 𝜉, normalize the following set, and return it if normalization succeeds: (𝗇𝖿(𝑃∪{𝜉\ℓ}),𝑆,𝖵𝖺𝗋({ℓ:𝜏∣𝜉})).
For 𝖾𝗆𝖻𝖾𝖽ℓ𝑢, infer (𝑃,𝑆,𝜏), choose fresh 𝛼,𝜉, solve 𝗌𝗈𝗅𝗏𝖾(𝑃∪{𝜉\ℓ};𝜏≐𝖵𝖺𝗋(𝜉))=(𝑈,𝑄), and return (𝑄,𝑆;𝑈,𝖵𝖺𝗋({ℓ:𝛼[𝑈]∣𝜉[𝑈]})). The fresh 𝛼 is intentionally unconstrained: embedding preserves a value from the old row and constructs no payload for the new ℓ alternative.
For a case, infer the scrutinee (𝑃0,𝑆0,𝜏0), choose fresh 𝛼,𝜉, and solve 𝗌𝗈𝗅𝗏𝖾(𝑃0∪{𝜉\ℓ};𝜏0≐𝖵𝖺𝗋({ℓ:𝛼∣𝜉}))=(𝑈0,𝑄0). Infer the matching branch and then the residual branch in the contexts Γ[𝑆0;𝑈0],𝑥:𝛼[𝑈0]andΓ[𝑆0;𝑈0;𝑆1],𝑦:𝖵𝖺𝗋(𝜉[𝑈0;𝑆1]), obtaining (𝑃1,𝑆1,𝜏1) and (𝑃2,𝑆2,𝜏2), respectively. Finally solve 𝗌𝗈𝗅𝗏𝖾(𝑄0[𝑆1;𝑆2]∪𝑃1[𝑆2]∪𝑃2;𝜏1[𝑆2]≐𝜏2)=(𝑉,𝑄) and return (𝑄,𝑆0;𝑈0;𝑆1;𝑆2;𝑉,𝜏2[𝑉]).
With the two supplies just specified and the solver’s printed clause order, 𝖶𝑟(Γ,𝑒) has at most one result. If two runs instead use different legal fresh names, there is a unique sort-preserving renaming of the names they allocate, fixing every variable in 𝐴, that carries one result and its induced typing derivation to the other.
Proof of Lemma 7.38 — Fixed-choice determinism of qualified W
Proof. Induct on 𝑒. Its outer constructor selects exactly one W clause, and the clause fixes the order of recursive calls, substitutions, predicate normalizations, and solver work lists. The induction hypotheses identify the recursive results. The ordered solver is deterministic by definition 4.23; the fixed quantifier and predicate orders determine generalization at a let. Lambda, application, and the row-form clauses each consume their fresh names before the next recursive call, while the case clause consumes the scrutinee names, then the matching branch supply, then the residual branch supply. Hence the fixed-supply results are equal. For two legal supplies, pair their first choices at each corresponding clause. The induction extends this pairing once per allocation, producing the stated renaming and leaving all variables in 𝐴 fixed. ◻
Consider 𝖼𝖺𝗌𝖾ℓ⟨ℓ=𝗓𝖾𝗋𝗈⟩𝗈𝖿{⟨ℓ=𝑥⟩↦(𝜆𝑧.𝑧)𝑥;𝑦↦(𝜆𝑧.𝑧)𝗓𝖾𝗋𝗈}. Injection gives a fresh tail 𝜉0 and the predicate 𝜉0\ℓ, so its result is 𝖵𝖺𝗋({ℓ:𝖭𝖺𝗍∣𝜉0}). The case clause chooses fresh 𝛼,𝜉1 and sends the equation 𝖵𝖺𝗋({ℓ:𝖭𝖺𝗍∣𝜉0})≐𝖵𝖺𝗋({ℓ:𝛼∣𝜉1}) to the solver. U-Type, U-Row, and I-Match expose the two equations 𝖭𝖺𝗍≐𝛼 and 𝜉0≐𝜉1. The fixed variable orientations return 𝑈0=[𝖭𝖺𝗍/𝛼,𝜉1/𝜉0],𝑄0={𝜉1\ℓ}. Hence the first branch is inferred under 𝑥:𝖭𝖺𝗍.
For (𝜆𝑧.𝑧)𝑥, choose fresh 𝛽 for the lambda domain and 𝛾 for the application result. The generated equation is 𝛽→𝛽≐𝖭𝖺𝗍→𝛾. Decomposition followed by the variable clauses gives 𝑆1=[𝖭𝖺𝗍/𝛽,𝖭𝖺𝗍/𝛾] and branch type 𝖭𝖺𝗍. The residual branch is then inferred under Γ[𝑈0;𝑆1],𝑦:𝖵𝖺𝗋(𝜉1[𝑈0;𝑆1])=𝑦:𝖵𝖺𝗋(𝜉1). For (𝜆𝑧.𝑧)𝗓𝖾𝗋𝗈, choose a disjoint fresh pair 𝛿,𝜀. The equation 𝛿→𝛿≐𝖭𝖺𝗍→𝜀 gives 𝑆2=[𝖭𝖺𝗍/𝛿,𝖭𝖺𝗍/𝜀] and the same branch type. The final equation is therefore 𝖭𝖺𝗍≐𝖭𝖺𝗍; deletion returns 𝑉=id. The algorithm returns ({𝜉1\ℓ},𝑈0;𝑆1;𝑆2,𝖭𝖺𝗍). Reading the postfix composite from left to right reproduces the computation: shape the scrutinee, solve the first branch, transport that result into the second branch, and finally equate the branch results. In a larger case the same positions are occupied by 𝑈0,𝑆1,𝑆2,𝑉, yielding 𝑆0;𝑈0;𝑆1;𝑆2;𝑉.
Let 𝑍 be a finite set of variables disjoint from ftv(Γ). A run of 𝖶𝑟(Γ,𝑒) may choose all fresh variables outside 𝑍; its returned substitution is then the identity on 𝑍.
Proof of Lemma 4.30 — Fresh support of qualified W
Proof. Induct on 𝑒 in the order of definition 4.29. Variable and constant instantiation return the identity, and a lambda changes only its freshly chosen domain variable and what the recursive call changes. At every sequential clause, pass to the later recursive call both 𝑍 and the variables of earlier results that are not free in its input context. The induction hypothesis fixes them. Solver calls fix the same protected set by lemma 4.26; composition preserves that property. The let and case clauses differ only in the number of such successive calls. Thus no clause changes a protected variable. ◻
The first component of a let is not discarded. Its predicates travel in 𝜎 and reappear, with its bound variables freshly instantiated, at each use of 𝑥; predicates mentioning the surrounding environment also remain in the returned residual set. This is qualified let polymorphism in exactly the same place where ordinary HM generalized type variables.
Infer the body of 𝗐𝗂𝗍𝗁𝖲𝖾𝖼𝗎𝗋𝖾=𝜆𝑟.{𝗌𝖾𝖼𝗎𝗋𝖾=𝗍𝗋𝗎𝖾∣𝑟}. Give 𝑟 fresh type 𝛼. The extension clause infers 𝗍𝗋𝗎𝖾:𝖡𝗈𝗈𝗅 and 𝑟:𝛼, then introduces fresh 𝜉. Its equation 𝛼≐𝖱𝖾𝖼(𝜉) and strictness condition give 𝑃={𝜉\𝗌𝖾𝖼𝗎𝗋𝖾},𝜏=𝖱𝖾𝖼(𝜉)→𝖱𝖾𝖼({𝗌𝖾𝖼𝗎𝗋𝖾:𝖡𝗈𝗈𝗅∣𝜉}). Generalization quantifies 𝜉, giving the scheme in example 4.14. Nothing in the calculation enumerates the untouched fields.
Write 𝑃⊢𝗋𝖾𝗀𝑆:Γ when every image under 𝑆 of a variable free in Γ is 𝑃-formed and every declaration of Γ[𝑆] has its scheme body formed under the union of 𝑃 with its own predicate set. A triple (𝑃,𝑆,𝜏) is regular for Γ when 𝑃 is normalized, 𝑃⊢𝗋𝖾𝗀𝑆:Γ, and 𝑃⊢𝜏𝗍𝗒𝗉𝖾. The decorated turnstile is a regularity check, not another entailment relation.
Suppose 𝑃0 is normalized, every row 𝜌 occurring in a predicate 𝜌\ℓ∈𝑃0 satisfies 𝑃0⊢𝜌𝗋𝗈𝗐, and 𝑃0⊢𝜏𝗍𝗒𝗉𝖾 for every type 𝜏 in the finite list ¯𝜏0. Suppose also that (𝑃,𝑆,𝜏) is regular for Γ, and the fresh variables allocated by this run of W are disjoint from ftv(𝑃0,¯𝜏0)∖ftv(Γ). Put 𝑌=ftv(Γ,𝑃0,¯𝜏0) and restrict 𝑆 to 𝑌, making it the identity elsewhere. Then 𝑆|𝑌:𝑃0→𝗇𝖿(𝑃0[𝑆]∪𝑃) is admissible, provided the displayed normalization succeeds, and the restriction agrees with 𝑆 on Γ,𝑃0,¯𝜏0.
Proof of Lemma 4.33 — Restricted-support transport
Proof. A variable of 𝑃0 or ¯𝜏0 changed by 𝑆|𝑌 either occurs in Γ, in which case regularity forms its image, or would be a fresh variable of this run, which the disjointness condition excludes. Every other nonidentity component of 𝑆 has been discarded. The normalized union entails 𝑃0[𝑆]. The global formation hypotheses therefore prove that the restriction is admissible. ◻
In applications of the lemma, the global fresh supply and lemma 4.30 establish its disjointness hypothesis before a later recursive call transports an earlier predicate set.
By lemma 4.11, an admissible 𝑈:𝑃→𝑄 sends a regular (𝑃,𝑆,𝜏) to the regular (𝑄,𝑆;𝑈,𝜏[𝑈]). The restricted-support lemma explains why later calls may instead transport only the finite support actually used by an earlier premise.
Suppose 𝑅:𝑃→𝑃′ is admissible, 𝜏′≡𝑃′𝜏[𝑅], and Γ′=Γ[𝑅]. Put 𝜎=GenΓ(𝑃⇒𝜏) and 𝜎′=GenΓ′(𝑃′⇒𝜏′). Freshen the prefix of 𝜎 away from 𝑅, and write 𝑅⋅𝜎 for the scheme obtained by applying 𝑅 only to the free variables of 𝜎; its displayed prefix remains bound. Every symbolic instance of 𝜎′ under a predicate context 𝑄 entailing its instantiated predicates is also a symbolic instance of 𝑅⋅𝜎. Moreover, each predicate of 𝑃𝗋 is carried to a predicate entailed by 𝑃′. Consequently a qualified typing under 𝑥:𝜎′ remains derivable after replacing that declaration by the more general 𝑥:𝑅⋅𝜎 in the already substituted surrounding context Γ[𝑅].
Proof of Lemma 4.34 — Qualified generalization calculation
Proof. Let 𝑋=ftv(𝑃,𝜏)∖ftv(Γ). Freshen 𝑋 away from the variables of 𝑅 and from the quantified prefix of 𝜎′. A symbolic instance of 𝜎′ consists of a substitution 𝐾 on that latter prefix and an entailment of 𝑃′[𝐾]. Instantiate each 𝑧∈𝑋 in 𝑅⋅𝜎 by 𝑧[𝑅;𝐾]. This substitution is sorted and formed. Since 𝑅:𝑃→𝑃′ and 𝑄 entails 𝑃′[𝐾], substitution stability gives 𝑄⊩𝗇𝖿(𝑃[𝑅;𝐾]); its result type is equal to 𝜏′[𝐾] by the displayed equality. Hence the chosen target instance is an instance of 𝑅⋅𝜎.
Each predicate in 𝑃𝗋 mentions a variable fixed by Γ, so admissibility makes its 𝑅-image a consequence of 𝑃′. For the final claim, induct on the body typing: only a leaf selecting 𝑥 changes, and the instance construction just given replaces it. ◻
Call a scheme context well formed when every declaration ∀𝑋.𝑃⇒𝜏 has 𝑃⊢𝜏𝗍𝗒𝗉𝖾, treating its unbound variables as parameters. For every such Γ in the pure let-language with the saturated row forms of definition 4.13, definition 4.15:
Soundness. If 𝖶𝑟(Γ,𝑒)=(𝑃,𝑆,𝜏), then (𝑃,𝑆,𝜏) is regular for Γ and Γ[𝑆]⊢𝑞𝑒:𝑃⇒𝜏;
Factorization. If Γ[𝑇]⊢𝑞𝑒:𝑃′⇒𝜏′, where 𝑇 is sorted and its images and Γ[𝑇] are formed under 𝑃′, then W succeeds and, for its result (𝑃,𝑆,𝜏), there is an admissible 𝑅:𝑃→𝑃′ such that 𝑇≡𝑃′𝑆;𝑅 on the variables of Γ, 𝜏′≡𝑃′𝜏[𝑅], and 𝑃′ entails every predicate of 𝗇𝖿(𝑃[𝑅]) (spelling out the predicate-entailment component of admissibility);
Failure. W fails exactly when there is no qualified typing of this form under any 𝑃′,𝑇,𝜏′.
Proof of Theorem 4.35 — Sound, complete, principal row inference
Proof. We prove soundness, regularity, and factorization simultaneously by induction on the term 𝑒, following the clause order of definition 4.29. In the factorization assertion, the comparison substitution is admissible from the returned predicate context to the target context; equality between composite substitutions means pointwise 𝑃′-equivalence as in definition 4.8. Final uses of Q-Conv in the target derivation are stripped before inversion and restored by transitivity.
The induction invariant at every recursive call has two support clauses. Let 𝑉sur contain the variables needed by the surrounding factorization and let 𝑉rec contain the variables in the recursive input. First, 𝑍=𝑉sur∖𝑉rec. Second, the recursive substitution fixes 𝑍, and its residual factor is extended on 𝑍 by the target solution. Lemma 4.26, Lemma 4.30 preserve the first clause; admissible composition and restricted-support transport preserve the second. Alongside these support clauses, regularity states that every returned type, predicate context, and substitution image is formed at its displayed target context. These are the recursive hypotheses used in every case below.
For each term form, inversion gives the equation in the middle column. Theorem 4.27 factors its target solution through the substitution returned by W; the last column records that factorization.
term form
constraint or fresh choice
factorization step
𝑥
fresh scheme instance
map fresh variables to the target instance
𝜆𝑥.𝑒
fresh domain 𝛼
extend the target map by 𝛼↦𝐴
𝑒1𝑒2
𝜏1≐𝜏2→𝛽
factor the target arrow equation
𝗅𝖾𝗍𝑥=𝑒1𝗂𝗇𝑒2
Gen(𝑃1⇒𝜏1)
qualified generalization, then the body factor
{}
none
identity
{ℓ=𝑒1∣𝑒2}
𝜏2≐𝖱𝖾𝖼(𝜉)
factor the record-row equation
𝑒.ℓ, 𝑒−ℓ
𝜏≐𝖱𝖾𝖼({ℓ:𝛼∣𝜉})
factor the exposed-field equation
⟨ℓ=𝑒⟩
fresh tail 𝜉
map 𝜉 to the target tail
𝖾𝗆𝖻𝖾𝖽ℓ𝑒
𝜏≐𝖵𝖺𝗋(𝜉)
factor the old-variant row
𝖼𝖺𝗌𝖾ℓ𝑒𝗈𝖿⋯
scrutinee exposure and branch equality
compose three solver factors
The context invariance of lemma 4.10 permits every recursive target typing to be rebuilt under the compared composite context. Thus, if the second recursive call returns 𝑆2 under 𝑃2, its fresh variables are disjoint from the earlier 𝑃1,𝜏1, and lemma 4.33 proves that 𝑆2|𝑌:𝑃1→𝗇𝖿(𝑃1[𝑆2]∪𝑃2) is admissible on the support of the transported premise. On every variable occurring in those premise types, 𝑆2|𝑌 and 𝑆2 agree, so those types may be written with 𝑆2. Subsequent solver maps compose by lemma 4.11.
For a variable, W replaces the quantified prefix by fresh variables. Rule Q-Var types this instance under its fresh predicate set. Any target leaf first applies the outer substitution 𝑇 to Γ(𝑥), then chooses formed types and strict rows for its quantified prefix. Define the residual factor to agree with 𝑇 on the free variables of the original declaration and send W’s fresh prefix variables to those instance choices. Thus 𝑆;𝑅 agrees with 𝑇 on Γ(𝑥); the target entailment premise proves that this map is admissible and gives the required type equality. Formation of the instance also proves regularity. Constants are identical, using Q-Const.
For 𝜆𝑥.𝑒0, W assigns 𝑥 a fresh 𝛼. The soundness induction hypothesis gives Γ[𝑆],𝑥:𝛼[𝑆]⊢𝑞𝑒0:𝑃⇒𝜏, so Q-Lam gives the returned 𝛼[𝑆]→𝜏. Conversely, inversion of a target lambda typing yields a domain 𝐴 and a typing of the body under 𝑥:𝐴. Extend the target substitution by 𝛼↦𝐴 and apply the body factorization. Removing the fresh component again gives the required factor on Γ. Regularity follows from the formed domain image and the body result type.
For 𝑒1𝑒2, the two soundness hypotheses, transported through 𝑆2, give types 𝜏1[𝑆2] and 𝜏2. The solver returns 𝑈 with 𝜏1[𝑆2;𝑈]≡𝑄𝜏2[𝑈]→𝛽[𝑈] and with 𝑄 entailing the transported premise constraints. Predicate weakening, Q-App, and Q-Conv therefore type the application at 𝛽[𝑈]. For factorization, invert a target application typing. The two recursive factorizations make its function and argument types an admissible solution of precisely the arrow equation and predicate set passed to the solver. The solving clause of theorem 4.27 factors that solution through 𝑈; composing the three factors gives 𝑅. The regularity hypotheses prove that 𝑃1[𝑆2]∪𝑃2 is formed, and the solver theorem proves that 𝑈 is admissible from that context to 𝑄; hence the returned composite and result type are regular.
At a let, soundness for the bound expression gives Γ[𝑆1]⊢𝑞𝑒1:𝑃1⇒𝜏1. Generalize the variables absent from Γ[𝑆1] and retain 𝑃𝗋1. Put 𝑄𝐿=𝗇𝖿(𝑃𝗋1[𝑆2]∪𝑃2). Regularity of the body call gives 𝑃2-formation derivations for the 𝑆2-images of variables free in Γ[𝑆1]. Let 𝐹⊆𝑃2 be the normalized set of assumption leaves used by those derivations. With 𝑃𝑆1=𝗇𝖿(𝑃1[𝑆2]∪𝐹), the restricted-support transport observation types 𝑒1 under 𝑃𝑆1. Predicate weakening moves the body hypothesis from 𝑃2 to 𝑄𝐿, and lemma 4.12 replaces its declaration by GenΓ[𝑆1;𝑆2](𝑃𝑆1⇒𝜏1[𝑆2]). Now Q-Let returns 𝑄𝐿: the residual part of 𝑃𝑆1 is 𝑃𝗋1[𝑆2]∪𝐹, already contained in or entailed by 𝑄𝐿. For principality, let 𝑅1 be the factor returned by the first recursive call. The target declaration for 𝑒1 is an instance of 𝑅1⋅GenΓ[𝑆1](𝑃1⇒𝜏1) by lemma 4.34; its non-generalizable predicates occur in the target let conclusion. Replace that target declaration by this more general substituted scheme, apply the body factorization, and compose. Regularity of 𝑆2 and retention of 𝑃𝗋1[𝑆2] form the returned context and result. This is the ordinary HM let argument with the residual set carried in every displayed step.
The empty record is immediate from Q-Empty. For {ℓ=𝑎∣𝑟}, transport the two soundness hypotheses through 𝑆2 and then 𝑈. The solver equation types the second term as 𝖱𝖾𝖼(𝜉[𝑈]), and 𝑄 entails 𝜉[𝑈]\ℓ. Rule Q-Extend yields 𝖱𝖾𝖼({ℓ:𝜏1[𝑆2;𝑈]∣𝜉[𝑈]}), the returned type. A target extension typing, after inversion, contains a payload type, a residual record row, and the corresponding lacks judgment. The two recursive factors turn these data into an admissible solution of the solver call in the extension clause. Principal constrained unification then yields a factor 𝑅 through which that solution factors.
For 𝑟.ℓ, soundness first gives Γ[𝑆]⊢𝑞𝑟:𝑃⇒𝜏. Solver soundness gives 𝜏[𝑈]≡𝑄𝖱𝖾𝖼({ℓ:𝛼[𝑈]∣𝜉[𝑈]}), and both sides are formed under 𝑄; solver admissibility transports the input typing from 𝑃 to 𝑄. The returned context also records the strictness predicate for 𝜉[𝑈]. Conversion followed by Q-Select therefore gives the returned type 𝛼[𝑈]. Conversely, inversion of a target selection exposes a unique field 𝖱𝖾𝖼({ℓ:𝐴∣𝜌}) and its lacks judgment. Together with the recursive factor for 𝑟, these are a solution of the displayed solver call; its principal factorization gives 𝑅. Restriction begins with the same exposure, but Q-Restrict returns 𝖱𝖾𝖼(𝜉[𝑈]). In the converse direction, the exposed target tail 𝜌 is therefore the image of the fresh 𝜉; no additional equation is needed.
For ⟨ℓ=𝑎⟩, use the induction hypothesis for the payload, choose the fresh 𝜉, and apply Q-Inject. Given any target injection typing, map that fresh 𝜉 to its residual row 𝜌. The target lacks premise is ℓ∉𝜌, so the map is admissible and the returned row and constraint are most general. For 𝖾𝗆𝖻𝖾𝖽ℓ𝑢, the recursive hypothesis types the old variant. The solver returns 𝑈 with 𝜉[𝑈]=𝜌 and records ℓ∉𝜌; Q-Embed adds the fresh alternative of type 𝛼[𝑈]. Inverting a target embedding derivation gives the same row equation and lacks predicate. The solver theorem therefore gives its factorization.
Finally consider a case. The first solver and the scrutinee hypothesis give Γ[𝑆0;𝑈0]⊢𝑞𝑢:𝑄0⇒𝖵𝖺𝗋({ℓ:𝛼[𝑈0]∣𝜉[𝑈0]}). The two recursive hypotheses type the branches in the two contexts printed in definition 4.29. Transport the first branch through 𝑆2; soundness of the last solver call equates its type with the second branch type and combines all three predicate sets. Rule Q-Case, followed by conversion, gives 𝜏2[𝑉]. Inverting a target case typing gives its selected field, residual variant, and common branch type. Principal factorization of the scrutinee solver fixes the two branch contexts; the two branch induction hypotheses then factor their typings; the final solver factors the equality of their result types. Their ordered composite is the required 𝑅.
In every row clause, the recursive triples are formed, transport proves each intervening substitution admissible, and solver soundness proves formation of the returned row and type. In the case clause the admissible substitutions are, successively, 𝑆0;𝑈0, 𝑆0;𝑈0;𝑆1, 𝑆0;𝑈0;𝑆1;𝑆2, and finally the admissible composite 𝑆0;𝑈0;𝑆1;𝑆2;𝑉. Thus no composite used in the soundness argument is merely a sorted raw substitution.
At the first failing recursive or solver call, the corresponding induction hypothesis or factorization theorem rules out the inverted target typing. Two clauses can instead fail while normalizing predicates. In the let clause, an inverted target typing supplies a formed normalized union of the residual definition predicates and body predicates, so the requested normalization must exist. In the injection clause, the target Q-Inject premise supplies a formed residual row lacking the injected label, so adjoining and normalizing that lacks predicate must also succeed. Hence either normalization failure rules out the corresponding target typing. Therefore W fails exactly when no qualified typing exists. ◻
This theorem is deliberately limited to pure let. If references from section 3.9 are added, the same conservative value restriction must be applied to qualified schemes; a row constraint does not make allocation pure.
★★☆ Run 𝖶𝑟 on 𝜆𝑥.𝖾𝗆𝖻𝖾𝖽𝖾𝗋𝗋𝗈𝗋⟨𝗈𝗄=𝑥⟩. Keep the independently fresh row tails introduced by injection and embedding until unification identifies them. Give the principal qualified scheme and explain why neither 𝗈𝗄 nor 𝖾𝗋𝗋𝗈𝗋 may occur in its residual tail.
So far, 𝜉\ℓ has been used as a proposition. It also tells the compiler where an ℓ field would be inserted into a canonical layout of 𝜉. Fix a decidable total order < on labels; store record fields in increasing order, and tag a variant by the zero-based position of its label in that order.
Elaboration reifies a proof of absence as an offset expression. The context Δ contains variables standing for assumed lacks predicates, and the judgment Δ⊢𝑑:𝜌\ℓ reads: from those assumptions, expression 𝑑 computes the insertion offset of ℓ in 𝜌. Thus predicates remain static propositions in the source, while their derivations produce integer evidence for the target.
An affine evidence expression in this chapter has one of the forms 𝑛 or 𝑑𝜉,ℓ+𝑛, with 𝑛∈ℕ. Addition of zero is omitted. We call evidence coherent when all derivations of the same lacks judgment normalize to the same such expression.
For 𝜌={𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀,𝗉𝗈𝗋𝗍:𝖭𝖺𝗍}, suppose 𝗁𝗈𝗌𝗍<𝗉𝗈𝗋𝗍<𝗌𝖾𝖼𝗎𝗋𝖾. The evidence for 𝜌\𝗌𝖾𝖼𝗎𝗋𝖾 is 0+1+1=2; inserting the field at offset 2 appends it to the canonical two-field array. If the labels are displayed in another row order, the same comparison count gives the same answer.
Let 𝑃 be normalized, and let Δ𝑃 contain one evidence variable for each predicate. Any two evidence derivations of Δ𝑃⊢𝑑:𝜌\ℓ yield the same affine expression in the variables of 𝑃. This expression is invariant under every adjacent exchange of distinct displayed fields, hence under every presentation of the same strict finite row. For a closed row, that expression is the numeral #{𝑚∈labels(𝜌)∣𝑚<ℓ}.
Proof. Induct on the number of displayed fields of 𝜌. At a row variable, normalization leaves exactly the unique matching evidence variable. At the empty row both derivations end at 0. At a displayed field 𝑚, the order decides uniquely whether to retain the tail expression or add one; apply the induction hypothesis to the tail. Adjacent exchange of two distinct labels preserves the number of labels smaller than ℓ, so the result is independent of the chosen row display. The closed formula follows by ending at the empty row. ◻
The target is a call-by-value let calculus with static type abstraction and explicit offset arguments. Its monotypes are 𝜃::=𝛼∣𝖭𝖺𝗍∣𝖡𝗈𝗈𝗅∣𝖲𝗍𝗋𝗂𝗇𝗀∣𝜃→𝜃∣𝖠𝗋𝗋𝖺𝗒(𝜌)∣𝖲𝗎𝗆(𝜌). A normalized predicate context 𝑃 indexes target formation. Write 𝑃⊢𝑡𝜃𝗍𝗒𝗉𝖾 when every row index in 𝜃 is a strict 𝑃-formed row and every field type is recursively target-formed. A substitution 𝑆:𝑃⇒𝑡𝑄 is target-admissible when it maps type variables to 𝑄-formed target types, maps row variables to 𝑄-formed strict rows, and 𝑄 entails every normalized predicate in 𝑃[𝑆]. For each assumption variable in Δ𝑃, that entailment has an evidence derivation in Δ𝑄; write ̂𝑆 for the simultaneous replacement by the canonical affine evidence expressions from lemma 4.37.
Fix the label order used by evidence. Let 𝗌𝗈𝗋𝗍(𝜌) sort the finite displayed prefix of a strict row by label, preserve its row-variable or empty tail, and recursively canonicalize field types. Let 𝖼𝖺𝗇𝖳𝗒(𝜃) apply this operation to every row index and recurse through arrows. Target type conversion is the exact rule
Δ;Γ⊢𝑡:𝜃𝖼𝖺𝗇𝖳𝗒(𝜃)=𝖼𝖺𝗇𝖳𝗒(𝜃′)
Δ;Γ⊢𝑡:𝜃′
T-Conv
Target types are therefore not an implicit quotient: the displayed judgment uses syntactic types plus this one conversion rule. Since sorting commutes with target-admissible substitution up to a final sort, formation and substitution preserve the premise of T-Conv. In particular, if 𝜌≡𝑃𝜌′, then 𝗌𝗈𝗋𝗍(𝜌)=𝗌𝗈𝗋𝗍(𝜌′), and both 𝖠𝗋𝗋𝖺𝗒 and 𝖲𝗎𝗆 convert between the two indices.
A target scheme has form ∀𝑋.𝖮𝖿𝖿(𝑃)⇒𝜃, where 𝖮𝖿𝖿(𝑃) is one offset-certificate argument for each member of the normalized set 𝑃. Order these members first by the fixed order of their row variables and then by the label order <. A target context may contain such schemes. The target term grammar is explicit: 𝑡::=𝑥∣𝑐∣𝜆𝑥.𝑡∣𝑡𝑡∣𝗅𝖾𝗍𝑥=𝑡𝗂𝗇𝑡∣Λ𝑋.𝜆¯𝑑.𝑡∣𝑡[𝑇]¯𝑑∣[]∣𝗅𝗈𝗈𝗄𝗎𝗉𝑑𝑡∣𝖽𝖾𝗅𝖾𝗍𝖾𝑑𝑡∣𝗂𝗇𝗌𝖾𝗋𝗍𝑑𝑡𝑡∣𝗍𝖺𝗀𝑑𝑡∣𝗐𝗂𝖽𝖾𝗇𝑑𝑡∣𝗌𝗉𝗅𝗂𝗍𝑑𝑡𝑡𝑡. The forms Λ𝑋.𝑡 and 𝑡[𝑇] are static type abstraction and application. Type abstraction erases; evidence lambdas execute at run time. Thus static instantiation may follow any term that evaluates to a scheme abstraction, while every data primitive is a saturated term former. The scheme rules are
Γ(𝑥)=𝜎
Δ;Γ⊢𝑥:𝜎
T-Var
𝑐:𝜃∈Σ0
Δ;Γ⊢𝑐:𝜃
T-Const
Δ;Γ⊢𝑡:∀𝑋.𝖮𝖿𝖿(𝑃)⇒𝜃𝑇issortedandformedΔ⊢¯𝑑:𝖮𝖿𝖿(𝑃[𝑇])
Δ;Γ⊢𝑡[𝑇]¯𝑑:𝜃[𝑇]
T-Inst
Δ,¯𝑑:𝖮𝖿𝖿(𝑃);Γ⊢𝑡:𝜃𝑋∩ftv(Δ,Γ)=∅
Δ;Γ⊢Λ𝑋.𝜆¯𝑑.𝑡:∀𝑋.𝖮𝖿𝖿(𝑃)⇒𝜃
T-Ev-Abs
Δ;Γ⊢𝑡1:𝜎Δ;Γ,𝑥:𝜎⊢𝑡2:𝜃
Δ;Γ⊢𝗅𝖾𝗍𝑥=𝑡1𝗂𝗇𝑡2:𝜃
T-Let
We identify a monotype 𝜃 with the empty scheme ∀∅.𝖮𝖿𝖿(∅)⇒𝜃. Instantiation of this empty scheme emits no target syntax: a monomorphic variable leaf elaborates to 𝑥, and a monomorphic constant leaf to 𝑐. The form 𝑡[𝑇]¯𝑑 is emitted only when the type prefix or evidence tuple is nonempty. Dually, an empty abstraction emits no wrapper: Λ∅.𝜆∅.𝑡 is represented simply by 𝑡. Thus lambda-bound variables and monomorphic let bindings remain ordinary; an expression such as (𝜆𝑥.𝑥)𝑐 does not reduce to a spurious static application of the base value 𝑐. Here the type variables 𝑋 are parameters in the premise of T-Ev-Abs. The monomorphic variable, constant, lambda, and application rules, with the evidence context copied unchanged, are Γ(𝑥)=𝜃Δ;Γ⊢𝑥:𝜃T−MVar𝑐:𝜃∈Σ0Δ;Γ⊢𝑐:𝜃T−MConstΔ;Γ,𝑥:𝜃⊢𝑡:𝜐Δ;Γ⊢𝜆𝑥.𝑡:𝜃→𝜐T−LamΔ;Γ⊢𝑡:𝜃→𝜐Δ;Γ⊢𝑢:𝜃Δ;Γ⊢𝑡𝑢:𝜐T−App. The binder Λ𝑋 is static and erases; the evidence lambdas remain ordinary run-time lambdas. Thus this is ML let polymorphism with explicit predicate arguments, not first-class polymorphism. Scheme abstractions are values, and their application computes by (Λ𝑋.𝜆¯𝑑.𝑡)[𝑇]¯𝑛⟶𝑡[𝑇/𝑋,¯𝑛/¯𝑑]. Together with the ordinary call-by-value let contraction, this is the whole extra dynamics for schemes.
Proof. For item 1, use the finite-map characterization of strict row equality in lemma 4.5. Both sorts list the same label–type map in the fixed label order and retain the same tail. For item 2, induct on 𝜃. The arrow case uses the two induction hypotheses. In an array or sum case, substitution may expose a displayed prefix from a row variable; the final call to 𝗌𝗈𝗋𝗍 merges that prefix with the surrounding fields, so both sides list the same finite map in the same order.
For item 3, induct on the target formation or typing derivation. Target-admissibility supplies formed images in variable and row-index cases. At an evidence premise, substitute the corresponding component of ̂𝑆; its derivation exists by the defining entailment of 𝑆:𝑃⇒𝑡𝑄. Every other syntax-directed rule follows from its induction hypotheses. In the T-Conv case, item 2 transports equality of canonical images through 𝑆. Replacement by an equal canonical image is one further application of T-Conv. Thus a raw sort-preserving map such as 𝑆(𝜉)={𝑎:𝖭𝖺𝗍,𝑎:𝖡𝗈𝗈𝗅} is excluded: its row image is not 𝑄-formed. ◻
Run-time values additionally include arrays [𝑤0,…,𝑤𝑛−1] and tagged payloads ⟨𝑖,𝑤⟩. If Δ⊢𝑑:𝜌\ℓ, the nullary array constructor and lookup rule are
Δ;Γ⊢[]:𝖠𝗋𝗋𝖺𝗒(𝜖𝜌)
T-Empty
Δ;Γ⊢𝑟:𝖠𝗋𝗋𝖺𝗒({ℓ:𝛼∣𝜌})Δ⊢𝑑:𝜌\ℓ
Δ;Γ⊢𝗅𝗈𝗈𝗄𝗎𝗉𝑑𝑟:𝛼
T-Lookup
The other five saturated rules are Δ;Γ⊢𝑟:𝖠𝗋𝗋𝖺𝗒({ℓ:𝛼∣𝜌})Δ⊢𝑑:𝜌\ℓΔ;Γ⊢𝖽𝖾𝗅𝖾𝗍𝖾𝑑𝑟:𝖠𝗋𝗋𝖺𝗒(𝜌)T−DeleteΔ;Γ⊢𝑎:𝛼Δ;Γ⊢𝑟:𝖠𝗋𝗋𝖺𝗒(𝜌)Δ⊢𝑑:𝜌\ℓΔ;Γ⊢𝗂𝗇𝗌𝖾𝗋𝗍𝑑𝑎𝑟:𝖠𝗋𝗋𝖺𝗒({ℓ:𝛼∣𝜌})T−InsertΔ;Γ⊢𝑎:𝛼Δ⊢𝑑:𝜌\ℓΔ;Γ⊢𝗍𝖺𝗀𝑑𝑎:𝖲𝗎𝗆({ℓ:𝛼∣𝜌})T−TagΔ;Γ⊢𝑢:𝖲𝗎𝗆(𝜌)Δ⊢𝑑:𝜌\ℓΔ;Γ⊢𝗐𝗂𝖽𝖾𝗇𝑑𝑢:𝖲𝗎𝗆({ℓ:𝛼∣𝜌})T−WidenΔ;Γ⊢𝑢:𝖲𝗎𝗆({ℓ:𝛼∣𝜌})Δ;Γ⊢𝑓:𝛼→𝛽Δ;Γ⊢𝑔:𝖲𝗎𝗆(𝜌)→𝛽Δ⊢𝑑:𝜌\ℓΔ;Γ⊢𝗌𝗉𝗅𝗂𝗍𝑑𝑢𝑓𝑔:𝛽T−Split. Each displayed primitive rule includes its evidence premise. There is therefore no term such as a half-applied 𝗅𝗈𝗈𝗄𝗎𝗉𝑑: it is neither syntax nor a possible stuck state.
Ground arrays use increasing label order. For 𝐴=[𝑎0,…,𝑎𝑛−1], write 𝐴[𝑖] for zero-based indexing when 0≤𝑖<𝑛, and write 𝐴∖𝑖 for deletion at such an index. For 0≤𝑖≤𝑛, define the length-increasing insertion operation 𝗂𝗇𝗌𝑖(𝑣,𝐴)=[𝑎0,…,𝑎𝑖−1,𝑣,𝑎𝑖,…,𝑎𝑛−1], with the empty prefix understood at 𝑖=0 and the empty suffix at 𝑖=𝑛. The primitive reductions are 𝗅𝗈𝗈𝗄𝗎𝗉𝑖𝐴⟶𝐴[𝑖],𝖽𝖾𝗅𝖾𝗍𝖾𝑖𝐴⟶𝐴∖𝑖,𝗂𝗇𝗌𝖾𝗋𝗍𝑖𝑣𝐴⟶𝗂𝗇𝗌𝑖(𝑣,𝐴),𝗍𝖺𝗀𝑖𝑣⟶⟨𝑖,𝑣⟩,𝗐𝗂𝖽𝖾𝗇𝑖⟨𝑗,𝑣⟩⟶{⟨𝑗,𝑣⟩𝑗<𝑖,⟨𝑗+1,𝑣⟩𝑖≤𝑗, and 𝗌𝗉𝗅𝗂𝗍𝑖⟨𝑗,𝑣⟩𝑓𝑔⟶⎧{
{⎨{
{⎩𝑓𝑣𝑗=𝑖,𝑔⟨𝑗,𝑣⟩𝑗<𝑖,𝑔⟨𝑗−1,𝑣⟩𝑖<𝑗. The compatible closure is left-to-right call by value. Besides the ordinary application and let contexts it evaluates the head of 𝑡[𝑇]¯𝑛, the single data argument of lookup, delete, tag, and widen, the two arguments of insert in their printed order, and the scrutinee and two branch functions of split in their printed order. These contexts contain only saturated primitive forms. Evidence expressions are stored in the canonical affine form 𝑑𝜉,ℓ+𝑛 or 𝑛; after a ground assignment they are numerals. Rule T-Empty types the empty array. A ground canonical array has type 𝖠𝗋𝗋𝖺𝗒(𝜌) exactly when its entries, in increasing label order, have the field types of 𝜌. Likewise ⟨𝑖,𝑣⟩:𝖲𝗎𝗆(𝜌) exactly when position 𝑖 of the sorted row 𝜌 carries the type of 𝑣. These clauses type every target value produced by the reductions above.
Translate rows and types simultaneously: 𝗅𝖺𝗒(𝜉)=𝜉,𝗅𝖺𝗒(𝜖𝜌)=𝜖𝜌,𝗅𝖺𝗒({ℓ:𝜏∣𝜌})=𝗌𝗈𝗋𝗍({ℓ:𝗅𝖺𝗒(𝜏)∣𝗅𝖺𝗒(𝜌)}),𝗅𝖺𝗒(𝖱𝖾𝖼(𝜌))=𝖠𝗋𝗋𝖺𝗒(𝗅𝖺𝗒(𝜌)),𝗅𝖺𝗒(𝖵𝖺𝗋(𝜌))=𝖲𝗎𝗆(𝗅𝖺𝗒(𝜌)),𝗅𝖺𝗒(𝜏→𝜐)=𝗅𝖺𝗒(𝜏)→𝗅𝖺𝗒(𝜐). Base types and variables are unchanged. Thus every row output of 𝗅𝖺𝗒 is canonical even when the source display is not, and row-equal displays have literally the same layout. An entailment derivation 𝑃⊩𝜌\ℓ determines, by definition 4.36, a target evidence term from the variables for 𝑃. Write it 𝑑𝑃,𝜌,ℓ. Send a source declaration 𝑥:∀𝑋.𝑃⇒𝜏 to 𝑥:∀𝑋.𝖮𝖿𝖿(𝑃)⇒𝗅𝖺𝗒(𝜏).
For example, 𝗅𝖺𝗒(𝖱𝖾𝖼({𝗉𝗈𝗋𝗍:𝖭𝖺𝗍,𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀}))=𝖠𝗋𝗋𝖺𝗒({𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀,𝗉𝗈𝗋𝗍:𝖭𝖺𝗍}), where the last row is in canonical label order.
The canonicalization function 𝖼𝖺𝗇(𝑡) identifies target terms that differ only by row order or affine presentation: it sorts every strict row in a target type or static argument and reduces each offset expression to affine normal form. This is meta-level normalization of annotations and evidence, not an additional run-time reduction. Its type component is exactly 𝖼𝖺𝗇𝖳𝗒, so the only typing equality it uses is the displayed T-Conv rule.
An open qualified judgment is unambiguous when ftv(𝑃)⊆ftv(Γ,𝜏). For a closed scheme ∀𝑋.𝑃⇒𝜏, this specializes to ftv(𝑃)⊆ftv(𝜏). The evidence-passing compiler accepts a principal inference result only when this condition holds. An ambiguous result remains a valid source typing and is covered by source safety, but it does not determine its evidence interface from the visible environment and result type.
For instance, the closed scheme ∀𝜉.𝜉\ℓ⇒𝖭𝖺𝗍 is ambiguous: its result contains no occurrence of 𝜉, so two ground instances may require different offsets although the visible result type is the same. The compiler rejects this evidence interface rather than choosing one offset invisibly.
The elaboration is defined on a typing derivation. At a variable or constant leaf, instantiate its scheme and apply the selected evidence tuple. Lambda and application elaborate homomorphically. At a let, replace the bound expression by Λ𝑋.𝜆¯𝑑:𝖮𝖿𝖿(𝑃1).𝑒†1, where 𝑋 is the generalized prefix; the body uses this scheme at each fresh instance. When 𝑋=𝑃1=∅, use 𝑒†1 itself, in accordance with the empty-scheme convention. Final row conversion emits no term. The empty constructor and the six evidence-carrying row forms become sourcetargetatevidence𝑑{}[]𝑟.ℓ𝗅𝗈𝗈𝗄𝗎𝗉𝑑𝑟𝑟−ℓ𝖽𝖾𝗅𝖾𝗍𝖾𝑑𝑟{ℓ=𝑣∣𝑟}𝗂𝗇𝗌𝖾𝗋𝗍𝑑𝑣𝑟⟨ℓ=𝑣⟩𝗍𝖺𝗀𝑑𝑣𝖾𝗆𝖻𝖾𝖽ℓ𝑣𝗐𝗂𝖽𝖾𝗇𝑑𝑣𝖼𝖺𝗌𝖾ℓ𝑣𝗈𝖿{𝑓;𝑔}𝗌𝗉𝗅𝗂𝗍𝑑𝑣𝑓𝑔. Here 𝑑=𝑑𝑃,𝜌,ℓ is the evidence term of definition 4.39 for the final normalized predicate context at that source rule; it is well defined up to the equality used by the target through lemma 4.37. In the case translation, 𝑓 and 𝑔 are the elaborated branch lambdas. These seven clauses, together with the four pure-let clauses, are the complete translation.
Keep the label order 𝗁𝗈𝗌𝗍<𝗉𝗈𝗋𝗍<𝗌𝖾𝖼𝗎𝗋𝖾. At the ground row 𝜌={𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀}, the evidence for 𝜌\𝗌𝖾𝖼𝗎𝗋𝖾 is 1. The source calculation is 𝗐𝗂𝗍𝗁𝖲𝖾𝖼𝗎𝗋𝖾{𝗁𝗈𝗌𝗍="𝚍𝚋"∣{}}⟶∗{𝗌𝖾𝖼𝗎𝗋𝖾=𝗍𝗋𝗎𝖾∣{𝗁𝗈𝗌𝗍="𝚍𝚋"∣{}}}. Its elaborated generalized value is Λ𝜉.𝜆𝑑:𝖮𝖿𝖿(𝜉\𝗌𝖾𝖼𝗎𝗋𝖾).𝜆𝑟.𝗂𝗇𝗌𝖾𝗋𝗍𝑑𝗍𝗋𝗎𝖾𝑟. Instantiating the static row, supplying its certified offset, and applying the resulting function gives the complete target trace. In this display [𝜌] is static type application, whereas ["𝚍𝚋"] is the array-value notation introduced in definition 7.50. ((Λ𝜉.𝜆𝑑.𝜆𝑟.𝗂𝗇𝗌𝖾𝗋𝗍𝑑𝗍𝗋𝗎𝖾𝑟)[𝜌]1)["𝚍𝚋"]𝑠𝑐ℎ𝑒𝑚𝑒−𝛽⟶(𝜆𝑟.𝗂𝗇𝗌𝖾𝗋𝗍1𝗍𝗋𝗎𝖾𝑟)["𝚍𝚋"]𝛽⟶𝗂𝗇𝗌𝖾𝗋𝗍1𝗍𝗋𝗎𝖾["𝚍𝚋"]𝑖𝑛𝑠𝑒𝑟𝑡⟶["𝚍𝚋",𝗍𝗋𝗎𝖾]. The last array is the sorted representation of the source result. The unmentioned 𝗁𝗈𝗌𝗍 field survives because the offset operation inserts rather than rebuilds the record from a closed list of labels.
The tempting lockstep theorem is false. If the source bound expression steps as 𝑒1⟶𝑒′1, then 𝗅𝖾𝗍𝑥=𝑒1𝗂𝗇𝑒2⟶𝗅𝖾𝗍𝑥=𝑒′1𝗂𝗇𝑒2, whereas its generalized target binding stores 𝗅𝖾𝗍𝑥=Λ𝑋.𝜆¯𝑑.𝑒†1𝗂𝗇𝑒†2; the bound scheme abstraction is already a target value, so a step-for-step simulation is false. We instead relate final values and, at a scheme, quantify over every ground instance and its canonical evidence.
Fix an admissible ground substitution. For each resulting ground type 𝜏, define a relation R𝜏 on source and target values. Base constants relate only to themselves. At a record type, sort the source map by label; it relates to an array when matching entries relate. At a variant type, let the unique field at ℓ have type 𝛼. Then ⟨ℓ=𝑣⟩ relates to ⟨𝑖,𝑤⟩ when 𝑣R𝛼𝑤 and 𝑖 counts the row labels below ℓ. Finally, 𝑓R𝜏→𝜐𝑔 when, for every 𝑎R𝜏𝑏, the two applications evaluate to values related by R𝜐.
A source value and a target scheme value are related at ∀𝑋.𝑃⇒𝜏 when, for every ground, formed instantiation of 𝑋 satisfying 𝑃, application of the target value to its canonical evidence tuple evaluates to a value related to the source value at the instantiated type. Source and target environments are related when their values are related at every declaration. This last clause is the invariant needed at a polymorphic let.
The fundamental lemma proves that every closed grounded source term and its elaboration reach related values. Purity and absence of recursion make this total value relation sufficient; references or recursion would require a partial or step-indexed relation instead.
Let 𝜌 be a strict ground row lacking ℓ, and let 𝑖 be the number of labels of 𝜌 smaller than ℓ. The source row operations and the target operations at offset 𝑖 preserve the ground value relation. More precisely:
the empty source record and the empty target array are related at 𝖱𝖾𝖼(𝜖𝜌) and 𝖠𝗋𝗋𝖺𝗒(𝜖𝜌), respectively;
related fields and records remain related after source extension and target insertion;
selection and lookup return related fields, while restriction and deletion return related residual records;
injection and tagging return related variants;
embedding and widening return related variants; and
suppose the distinguished and residual source branch functions are related to target functions 𝑓 and 𝑔 at 𝛼→𝛽 and 𝖵𝖺𝗋(𝜌)→𝛽, respectively. Then source case analysis and target split evaluate to related results.
Proof. The first clause is the empty-list instance of the record relation. Sort the labels of 𝜌 as ℓ0<⋯<ℓ𝑛−1. By definition of the record relation, a source record 𝑅={ℓ0=𝑣0,…,ℓ𝑛−1=𝑣𝑛−1} relates to 𝐴=[𝑤0,…,𝑤𝑛−1] precisely when 𝑣𝑗R𝑤𝑗 at the field type carried by ℓ𝑗. Inserting a related field 𝑣R𝛼𝑤 at position 𝑖 gives the sorted lists {ℓ=𝑣∣𝑅}R𝗂𝗇𝗌𝑖(𝑤,𝐴). This proves the extension clause. In a record already containing ℓ at position 𝑖, source selection scans to the same field that 𝗅𝗈𝗈𝗄𝗎𝗉𝑖 indexes, and source restriction removes the same entry that 𝖽𝖾𝗅𝖾𝗍𝖾𝑖 removes. This proves the three record-operation clauses.
Injection is immediate: the new label has position 𝑖, so ⟨ℓ=𝑣⟩ relates to ⟨𝑖,𝑤⟩. For embedding, let an old label 𝑚 have position 𝑗 in 𝜌. Its position in {ℓ:𝛼∣𝜌} is 𝑗 when 𝑚<ℓ, and 𝑗+1 when ℓ<𝑚. These are exactly the two clauses of 𝗐𝗂𝖽𝖾𝗇.
It remains to check case analysis. Let the larger-row target value be ⟨𝑗,𝑤⟩. There are three possibilities. If 𝑗=𝑖, its source mate is ⟨ℓ=𝑣⟩; source case takes the distinguished branch and target split reduces to 𝑓𝑤. The arrow relation for the two distinguished branch functions gives related results. If 𝑗<𝑖, the carried source label is smaller than ℓ and already has residual-row position 𝑗; split passes ⟨𝑗,𝑤⟩ to 𝑔. If 𝑖<𝑗, removing the inserted position lowers the residual tag to 𝑗−1; split passes ⟨𝑗−1,𝑤⟩ to 𝑔. In the last two cases this target value is precisely the mate of the whole residual source variant passed to the residual branch. Its arrow relation gives the required result. This exhausts the six operations. ◻
Proof of Lemma 4.44 — Typing of evidence elaboration
Proof. Induct on D. The Q-Empty case elaborates to ([ ]) and uses T-Empty. In Q-Var, translate each entailed instantiated predicate to its evidence expression; T-Var followed by T-Inst then has exactly the displayed result type when the scheme is genuine; for the empty scheme the convention in definition 4.38 uses T-Var alone. The nullary Q-Const instances use the target constant rule. Lambda and application use the ordinary target rules. Conversion emits no term: item 1 of lemma 7.49 makes the two layouts identical, and T-Conv transports the target typing.
For Q-Let, if the generalized prefix and 𝑃1 are both empty, the two induction hypotheses and T-Let give the ordinary monomorphic let directly. Otherwise apply the first induction hypothesis under fresh evidence variables for all of 𝑃1 and use T-Ev-Abs. This types Λ𝑋.𝜆¯𝑑:𝖮𝖿𝖿(𝑃1).D†1:∀𝑋.𝖮𝖿𝖿(𝑃1)⇒𝗅𝖺𝗒(𝜏1). The evidence context for 𝗇𝖿(𝑃𝗋1∪𝑃2) projects the tuple needed by the body derivation. The second induction hypothesis and T-Let finish the case. The abstraction takes the whole 𝑃1 tuple; its residual part also remains available outside the let.
For extension, the two induction hypotheses type the field value and record array. The normalized conclusion contains an evidence term for 𝜌\ℓ, so the type of 𝗂𝗇𝗌𝖾𝗋𝗍 gives the translated result. Selection and restriction use lookup and delete with the same certificate. Injection and embedding use tag and widen. In the case rule, the two branch hypotheses type the lambdas 𝜆𝑥.D†1 and 𝜆𝑦.D†2 at the two argument types of split; T-Split then returns 𝗅𝖺𝗒(𝛽). ◻
Let D derive Γ⊢𝑞𝑒:𝑃⇒𝜏. Let 𝐺:𝑃→∅ be an admissible ground substitution, let ¯𝑛 be its canonical evidence tuple, and let source and target environments 𝛾,𝜂 be related at Γ[𝐺]. Then there are values 𝑣,𝑤 with 𝑒[𝛾]⟶∗𝑣,𝐺(D†)[¯𝑛/Δ𝑃,𝜂]⟶∗𝑤,𝑣R𝜏[𝐺]𝑤.
Proof. Induct on D. The Q-Empty case evaluates to the empty source record and the empty target array; they are related by the first clause of lemma 4.43. At a variable leaf, the related-environment hypothesis relates the source value to the target scheme value. Applying the chosen ground instance and its canonical evidence tuple is exactly the scheme clause of definition 4.42. A constant evaluates to itself on both sides and is related at its declared base type.
For a lambda, take arbitrary related arguments 𝑎 and 𝑏. Substitution extends the two environments by 𝑥↦𝑎 and 𝑥↦𝑏; the induction hypothesis for the body gives related results. Hence the two lambda values satisfy the arrow clause. In an application, the two induction hypotheses give 𝑓R𝛼→𝛽𝑔,𝑎R𝛼𝑏. The defining arrow clause, not a new operational argument, says that 𝑓𝑎 and 𝑔𝑏 evaluate to values related at 𝛽.
At a let with empty generalized prefix and 𝑃1=∅, the first induction hypothesis gives related source and target values for the bound expression; extend the two environments by those values and apply the body induction hypothesis. This is the ordinary monomorphic-let case.
Otherwise first extend the ground map to the generalized variables: send fresh type variables to 𝖭𝖺𝗍 and fresh row variables to the empty row. No row extension is required, because the empty row satisfies every normalized lacks predicate on those fresh variables. Together with the fixed ambient assignments, this is an admissible ground map for the first premise. Its induction hypothesis evaluates 𝑒1[𝛾] to a source value 𝑣. Now take an arbitrary ground instantiation satisfying 𝑃1 and apply the same induction hypothesis. It produces some source value 𝑣𝑇 and a target value obtained from (Λ𝑋.𝜆¯𝑑.D†1)[𝑇]¯𝑛1. The generalized variables are absent from the source environment and term, so both source evaluations start from the same expression. By lemma 7.28, 𝑣𝑇=𝑣. Since the ground instantiation was arbitrary, the fixed source value 𝑣 and the target abstraction satisfy the universally quantified scheme clause of definition 4.42. Extend 𝛾,𝜂 by them and apply the second induction hypothesis. The source evaluates 𝑒1 once before substitution; the target abstraction may evaluate its pure body at each instance. The scheme relation makes these evaluations agree.
For extension, selection, restriction, injection, and embedding, apply the subterm induction hypotheses and the corresponding clause of lemma 4.43. In the case rule the scrutinee induction hypothesis proves that the two evaluated scrutinees are related variants. Apply the two branch induction hypotheses under arbitrary related values for 𝑥, respectively 𝑦; by the lambda argument above, the elaborated branch lambdas are related to the two source branch functions. The case-analysis clause of lemma 4.43 now treats all three possible tag comparisons. In particular, the residual branch receives the unchanged tag below the inserted position and the decremented tag above it, exactly as the sorted residual row requires. Conversion changes only a row display, and lemma 4.5 leaves the relation unchanged. ◻
its elaboration has the target type stated in lemma 4.44;
if Γ is empty and 𝐺:𝑃→∅ is admissible and ground, then there are values 𝑣,𝑤 such that 𝑒⟶∗𝑣,𝐺(D†)[¯𝑛/Δ𝑃]⟶∗𝑤,𝑣R𝜏[𝐺]𝑤;
if D′ changes only the derivations of normalized lacks entailments or the display order of equal strict rows, then 𝖼𝖺𝗇(D†)=𝖼𝖺𝗇((D′)†) as literal target syntax.
Proof of Theorem 4.46 — Preservation, adequacy, and coherence of evidence passing
Proof. Item 1 is lemma 4.44. For item 2, apply lemma 4.45 to the two empty environments. For item 3, induct on the common source rule tree. Variable and constant leaves select the same scheme instance; lambda and application follow from their induction hypotheses. At a let, the generalized variable set is unchanged because row display and lacks derivations do not change free variables; rename the two fresh static binders to the fixed quantifier order and apply both induction hypotheses. At Q-Conv, canonical target indices agree by lemma 7.49(1). For every record or variant rule, lemma 4.37 gives the same affine evidence expression, the induction hypotheses give the same operands, and lemma 4.5 gives the same canonical row index. These cases cover the rule tree, so the canonical target terms are literal syntactic equals. ◻
Suppose qualified W returns an unambiguous principal result for 𝑒. Run W with the fixed work-list, quantifier, label, and predicate orders of this chapter, then elaborate its induced typing derivation. The resulting canonical target term is unique up to renaming of freshly allocated static variables.
Proof of Corollary 7.61 — Coherent principal compilation
Proof. The solver clauses are ordered, and lemma 7.38 gives determinism of W up to the unique fresh renaming that fixes the input. The unambiguity condition requires every residual evidence parameter to occur in the visible type interface. Finally, item 3 of theorem 4.46 removes the remaining choices of lacks derivation and row display order. ◻
Principal inference fixes the residual row and field-type choices; administrative coherence then removes the remaining choices of row display and lacks derivation. For a fixed source typing tree, the resulting target programs have the same canonical syntax. Predicate weakening can change the evidence tuple, so comparing arbitrary derivations additionally requires the principal-inference choice.
One should not strengthen adequacy to the literal claim that every source step is simulated by one or more target steps. A generalized source let may reduce its bound expression, whereas its evidence-passing translation stores an evidence abstraction, already a target value. The logical relation permits these administrative steps to occur in a different order while still requiring the same ground result. For each primitive record or variant contraction, the proof above does give a direct target simulation; the weaker global statement is needed only because of qualified let generalization.
★☆☆ Assume label order 𝖾𝗋𝗋𝗈𝗋<𝗁𝗈𝗌𝗍<𝗈𝗄<𝗉𝗈𝗋𝗍. Compute the evidence for each of {𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀,𝗉𝗈𝗋𝗍:𝖭𝖺𝗍}\𝗈𝗄,{𝗈𝗄:𝛼,𝖾𝗋𝗋𝗈𝗋:𝖲𝗍𝗋𝗂𝗇𝗀}\𝗁𝗈𝗌𝗍. Then trace the numeric tag changes when an 𝗈𝗄 variant is embedded first past 𝗁𝗈𝗌𝗍 and then past 𝖾𝗋𝗋𝗈𝗋.
The completed calculus lets us compare alternatives without confusing their theorems.
Presence flags
Rémy represents each label by a presence descriptor: 𝗉𝗋𝖾(𝜏) when the field is present and 𝖺𝖻𝗌 when absent, with row variables standing for the remaining descriptors. In that notation, strict extension changes the ℓ descriptor from 𝖺𝖻𝗌 to 𝗉𝗋𝖾(𝜏). Our judgment 𝜉\ℓ carries the same negative fact separately from the positive row structure.
For a finite label universe, the translation is literal: send {ℓ:𝜏∣𝜌} to the flag map obtained from 𝜌 by setting ℓ to 𝗉𝗋𝖾(𝜏), and send every absent label to 𝖺𝖻𝗌. Lemma 4.5 shows that permutation does not change this map. With an unbounded label set, Rémy’s sorted record algebra is more economical than writing an infinite flag vector. Its unitary unification theorem—every solvable problem has one most general solution up to mutual instantiation—and the Gaster–Jones theorem reach the same principal-inference goal through different syntax; we do not combine their unification rules.
Scoped duplicate labels
If duplicate labels are retained, extension need not require a lacks predicate. Selection and restriction act on the first matching label, and row equality swaps adjacent fields only when their labels differ. Then 𝜆𝑟.{𝗌𝖼𝗋𝖺𝗍𝖼𝗁=𝗓𝖾𝗋𝗈∣𝑟} has an unconstrained row-polymorphic type even when 𝑟 already contains 𝗌𝖼𝗋𝖺𝗍𝖼𝗁; the old field remains behind the new one. In our calculus, the same term requires 𝜉\𝗌𝖼𝗋𝖺𝗍𝖼𝗁, while replacement is written explicitly as update.
Neither choice is a notational variant of the other. The duplicate-label calculus admits the row rejected after definition 4.3; our canonical-forms proof uses its impossibility. Its simpler unification does not prove theorem 4.35, and our insertion theorem does not prove principality for its equality. Leijen’s scoped-label calculus gives the alternative rules and proof boundary.
Width subtyping
For this comparison only, take the two rules 𝐽⊆𝐼{ℓ𝑖:𝜏𝑖}𝑖∈𝐼<:{ℓ𝑗:𝜏𝑗}𝑗∈𝐽WidthΓ⊢𝑒:𝐴𝐴<:𝐵Γ⊢𝑒:𝐵Sub, where a shared label has the same field type on both sides. These rules are not part of the row calculus proved above; they define the small comparison system used in this paragraph and the following exercise. With width subtyping, one may declare {𝗉𝗈𝗋𝗍:𝖭𝖺𝗍,𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀}<:{𝗉𝗈𝗋𝗍:𝖭𝖺𝗍} and type 𝜆𝑟.𝑟.𝗉𝗈𝗋𝗍 at the fixed function type {𝗉𝗈𝗋𝗍:𝖭𝖺𝗍}→𝖭𝖺𝗍. Subsumption then accepts larger arguments. Row polymorphism instead gives a quantified tail and needs no subsumption judgment.
The difference appears in the result of extension. Subtyping can forget fields to view a large record as a smaller one, but the type {𝗉𝗈𝗋𝗍:𝖭𝖺𝗍}→{𝗌𝖾𝖼𝗎𝗋𝖾:𝖡𝗈𝗈𝗅,𝗉𝗈𝗋𝗍:𝖭𝖺𝗍} does not say that an arbitrary input field survives. The row type ∀𝜉.(𝜉\𝗌𝖾𝖼𝗎𝗋𝖾)⇒𝖱𝖾𝖼(𝜉)→𝖱𝖾𝖼({𝗌𝖾𝖼𝗎𝗋𝖾:𝖡𝗈𝗈𝗅∣𝜉}) does. Row inclusion and subtyping can coexist, but they solve different typing problems.
★★☆ First derive width subtyping for applying 𝗉𝗈𝗋𝗍𝖮𝖿 to a record with 𝗁𝗈𝗌𝗍, 𝗉𝗈𝗋𝗍, and 𝗌𝖾𝖼𝗎𝗋𝖾 fields. Then give the row-polymorphic instantiation. Explain why width subtyping alone cannot recover the precise result type of adding a 𝗍𝗂𝗆𝖾𝗈𝗎𝗍 field while preserving all three inputs.
The strict calculus fixes every operation label in the term syntax. To make a label a value, add a label sort, label variables 𝑘, singleton types 𝖫𝖺𝖻(𝑘), and lacks predicates with label-variable right sides. The judgment 𝑃⊩𝜉\𝑘 says that the row assigned to 𝜉 has no field at the label denoted by 𝑘. The selection rule is 𝑃⊩𝜉\𝑘Γ⊢𝑝:𝖫𝖺𝖻(𝑘)Γ⊢𝑟:𝖱𝖾𝖼({𝑘:𝛼∣𝜉})Γ⊢𝗌𝖾𝗅𝖾𝖼𝗍𝖠𝗍𝑝𝑟:𝛼Label−Select. The singleton index connects the run-time label value to the row component selected by the operation. Consequently the generic selector has scheme ∀𝑘,𝛼,𝜉.(𝜉\𝑘)⇒𝖫𝖺𝖻(𝑘)→𝖱𝖾𝖼({𝑘:𝛼∣𝜉})→𝛼. A plain type 𝖫𝖺𝖻𝖾𝗅 would not suffice: it would forget which row component the value denotes. An existential package ∃𝑘.𝖫𝖺𝖻(𝑘) hides its index, but two package openings alone do not prove that their witnesses differ: both packages may contain the same label. A generative allocator needs an operational freshness invariant. For example, let 𝑊 be the finite set of allocated labels; allocation chooses 𝑘∉𝑊, returns a package hiding 𝑘, and continues with 𝑊∪{𝑘}. Two sequential allocations are distinct because the second choice excludes the first. This subsection uses only first-class selection and does not claim that generative calculus.
First-class labels still do not supply generic traversal. For a type operator 𝐹, write 𝖬𝖺𝗉(𝐹,𝜌) for the row obtained by applying 𝐹 to every field type in 𝜌. A generic dispatcher would have type 𝖱𝖾𝖼(𝖬𝖺𝗉(𝜆𝛼.𝛼→𝛾,𝜌))→𝖵𝖺𝗋(𝜌)→𝛾. It must inspect whichever label the variant carries and select the matching function from the record. The strict calculus can eliminate one statically named label, but it has neither 𝖬𝖺𝗉 on rows nor induction over an unknown row. Thus repeated uses of its case rule implement the dispatcher only after the finite labels are known. This obstruction separates first-class selection from genuinely generic programming over extensible data.
One concatenation program in two row theories
Consider symmetric concatenation 𝑚⋆𝑛, defined only when the input records have disjoint labels, and the program 𝜆𝑚.𝜆𝑛.(𝑚⋆𝑛).𝗑. The selected field may come from either input. This single term exposes a constraint that a lacks predicate on one named extension cannot express.
One constraint presentation regards a row as a label-indexed family of descriptors 𝖠𝖻𝗌 or 𝖯𝗋𝖾(𝜏). If 𝐿 is a finite or cofinite set of labels, write 𝐿:𝐶(𝜑1,…,𝜑𝑛) when the component constraint 𝐶 holds pointwise for every label in 𝐿. Define 𝖬𝖾𝗋𝗀𝖾(𝑎,𝑏,𝑐) by the two successful cases 𝖬𝖾𝗋𝗀𝖾(𝖯𝗋𝖾(𝜏),𝖠𝖻𝗌,𝖯𝗋𝖾(𝜏)),𝖬𝖾𝗋𝗀𝖾(𝖠𝖻𝗌,𝖯𝗋𝖾(𝜏),𝖯𝗋𝖾(𝜏)), together with 𝖬𝖾𝗋𝗀𝖾(𝖠𝖻𝗌,𝖠𝖻𝗌,𝖠𝖻𝗌); there is no case with two present inputs. The filtered constraint 𝖫𝖺𝖻𝖾𝗅𝗌:𝖬𝖾𝗋𝗀𝖾(𝜌1,𝜌2,𝜌3),{𝗑}:𝜌3=𝖯𝗋𝖾(𝛼) types (7.2). The singleton filter states the selection fact, while the all-label filter states disjoint concatenation.
An abstract row theory packages the same information as two predicates. Write 𝖢𝗈𝗆𝖻𝗂𝗇𝖾(𝜌1,𝜌2,𝜌3) when the first two rows combine to the third, and write 𝖢𝗈𝗇𝗍𝖺𝗂𝗇𝗌({𝗑:𝛼},𝜌3) when the third row contains the selected field. The program then has the qualified type ∀𝜌1,𝜌2,𝜌3,𝛼.(𝖢𝗈𝗆𝖻𝗂𝗇𝖾(𝜌1,𝜌2,𝜌3),𝖢𝗈𝗇𝗍𝖺𝗂𝗇𝗌({𝗑:𝛼},𝜌3))⇒𝖱𝖾𝖼(𝜌1)→𝖱𝖾𝖼(𝜌2)→𝛼. This formulation can be interpreted by different row-combination algebras; the strict system developed in this chapter fixes only single-field insertion with a lacks side condition.
There is no direct encoding of this program that uses only strict selection from one fixed input and finitely many extensions at labels known in the program text. Instantiate once with 𝗑 present only in 𝜌1, and once with it present only in 𝜌2. Both instances satisfy the two displayed constraint accounts. A strict selection from a fixed input fails one of them, while selecting from the result would require the absent concatenation primitive. Expanding concatenation into extensions is possible for a known finite row, but not uniformly for two unknown row tails. Thus the two instances separate that direct-encoding class. They do not exclude arbitrary whole-program translations into a richer target. The comparison theories add a row-wide combination relation, not merely alternate notation for 𝜉\ℓ.
Bibliographic notes.
The qualified types, mutually recursive unification and insertion procedures, principal inference pattern, and offset-evidence idea follow Gaster and Jones [GJ96]. Their constructor syntax permits raw duplicate rows and relies on predicates at operation sites. Our normalized predicate contexts and strict formation are therefore an adaptation, not a verbatim reproduction. The constrained insertion theorem above proves the correspondence needed here: every admissible strict solution factors through the same fresh-tail exposure, and every generated residual predicate is precisely a strict-formation obligation. We proved termination, factorization, safety, and the restricted administrative coherence of remark 7.62 for this adapted presentation rather than importing those claims. Gaster and Jones give the two evidence-parameter rules and their offset-entailment rules in Section 5.1 and Figure 6, then explicitly omit the complete translation for reasons of space. We therefore define the target in definition 4.38 and the translation in definition 4.39, definition 4.40. The required properties are proved in theorem 4.46.
Rémy’s record algebra and transfer from unitary unification to principal ML typing are in [Ré91]. Scoped duplicate labels are compared from Leijen [Lei05]; those sections respectively give record operations and equality, variants, and the formation (there called kinding) and unification metatheory. That paper’s published proof is not used for the strict calculus. These sources make different choices about duplicates and predicates, so their theorems are not pooled.
The singleton label indices, row-equality predicates, and generativity boundary in subsection 7.8.4 follow the first-class-label calculus of Leijen [Lei04]. The row-mapping dispatcher isolates the additional generic traversal studied by Hubers and Morris [HM23a]. The filtered component account of subsection 7.8.5 is reconstructed from Pottier [Pot03]; the combination and containment predicates are the abstract row-theory interface of Morris and McKinna [MM19]. We use these works for comparison only. The strict safety, inference, and elaboration proofs do not import their metatheorems.
Boundary and further problems
The fields in this chapter have ordinary, nondependent types. If a later field type may mention the value of an earlier field, permutation is no longer a bare exchange: the type must be transported when the dependency crosses the exchange. Likewise, row unification would have to solve terms inside types, not just the sorted first-order equations of definition 4.23. We therefore make no dependent-row claim here. Any dependent extension must state its own substitution, equality, decidability, and inference theorems.
Suggested first pass.
Begin with exercise 4.10. It is a board-sized calculation that tests both admissible grounding and the exact point at which a bad instance fails. Then reconstruct the two factor maps in exercise 4.11; these two problems form the first pass. Use exercise 4.9 as the next synthesis problem and exercise 7.12 as the implementation project. The final two problems test the duplicate-label and evidence-ambiguity boundaries.
★★☆ Let 𝐺 instantiate 𝜉 by {𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀} in the principal type of 𝗐𝗂𝗍𝗁𝖲𝖾𝖼𝗎𝗋𝖾. Write the complete ground typing and reduce its application to {𝗁𝗈𝗌𝗍="𝚍𝚋"∣{}}. Repeat with an attempted instantiation containing 𝗌𝖾𝖼𝗎𝗋𝖾:𝖭𝖺𝗍 and identify the exact failed normalization step.
★★☆ Suppose (𝑈,𝑄𝑈) and (𝑉,𝑄𝑉) are principal solutions of the same row equation problem over 𝑃. Use their two factorization properties to construct admissible maps 𝑅:𝑄𝑈→𝑄𝑉 and 𝑆:𝑄𝑉→𝑄𝑈 with 𝑉≡𝑄𝑉𝑈;𝑅 and 𝑈≡𝑄𝑈𝑉;𝑆 on the problem variables. Give a two-label example where the printed fresh tail names, residual predicate contexts, and field orders differ although this mutual instance conclusion holds.
Hint. Apply the factorization property of 𝑈 to the solution 𝑉, then the factorization property of 𝑉 to 𝑈. For the example, use the crossing equation of example 4.24 twice, with different fresh tail names and different legal exchange sequences.
★★★ Assume ℓ≠𝑚 and a tail 𝜉 lacking both labels. Infer the principal scheme of 𝜆𝑎.𝜆𝑏.𝜆𝑟.{ℓ:=𝑎∣{𝑚:=𝑏∣𝑟}}. Show by row permutation that reversing the two updates gives the same result type. Does the operational result have the same finite map? Give the two restriction-and-extension calculations.
★★★Practical project.row-inferencer Work in artifacts/ch7-rows/corpus.kp. Implement the four insertion and six solver clauses of definition 4.23 in their printed order, including U-Delete. Return the actual clause trace with every successful solver result; do not predict it by traversing the input a second time. Preserve the invariant that the returned substitution is admissible from the input predicate context to the normalized output context.
The accepted crossing run must return and print 𝚄-𝚁𝚘𝚠->𝙸-𝚂𝚔𝚒𝚙->𝙸-𝚅𝚊𝚛->𝚄-𝚅𝚊𝚛->𝚄-𝙳𝚘𝚗𝚎. Apply its substitution to both input rows, check that both substituted rows are strictly formed under the returned predicates, and then check contextual row equality. Also test syntactic identity deletion, principal factorization, termination within the input-derived bound, record and variant operations, fresh supply, qualified generalization at two instances, empty insertion, the occurs check, duplicate formation, and the nonprincipal closed-tail mutant. Run the four commands in Appendix F and require all eleven PASS lines, the printed summary, and audit output []. Finally apply mutant-accept-empty-insertion.patch to a disposable copy: it must still check and must fail the empty-insertion oracle. These finite runs are implementation evidence; they do not prove termination, factorization, principality, coherence, inference correctness, or safety.
★★★ Design the record fragment of a scoped-duplicate calculus, rather than changing formation alone. Make the following four changes.
Delete the lacks premise from each extension rule: 𝑅𝑜𝑤−𝐸𝑥𝑡,𝑄−𝐸𝑥𝑡𝑒𝑛𝑑.
Permit repeated labels in record values.
Let selection and restriction act on the first displayed occurrence.
Continue to swap only adjacent distinct labels.
Which sentence in the proof of lemma 4.21 now becomes false? Repair the canonical-forms statement for this calculus, and state why the repaired lemma does not justify the strict update equation without specifying which occurrence is removed.
Do not extend this answer to variants. A scoped-duplicate variant calculus must additionally change embedding, represent the nesting level of equal labels at run time, and give matching case rules; the label-only variant values of definition 4.17 cannot express that information.
Hint. Induct on the displayed record spine. Selection returns the first matching occurrence, whereas restriction removes that occurrence and leaves every later occurrence available.
★★★ Consider the scheme ∀𝜉.(𝜉\ℓ)⇒𝖭𝖺𝗍. Its result type does not determine which evidence is required. Show that all its ground instances have the same source type but may elaborate to functions expecting offsets for different rows. State the usual unambiguity condition for an open judgment and its closed-scheme specialization: ftv(𝑃)⊆ftv(Γ,𝜏),ftv(𝑃)⊆ftv(𝜏). Now run 𝖶𝑟 on 𝖼𝖺𝗌𝖾ℓ⟨ℓ=𝗓𝖾𝗋𝗈⟩𝗈𝖿{⟨ℓ=𝑥⟩↦𝗓𝖾𝗋𝗈;𝑦↦𝗓𝖾𝗋𝗈}. Show that its principal result contains a fresh predicate 𝜉\ℓ although its result type is 𝖭𝖺𝗍. Thus unambiguity is an additional acceptance condition, not an invariant of the inference clauses. Give two possible repairs: reject such ambiguous principal results, or choose and document a default ground row when evidence is elaborated.
Hint. Give injection a fresh tail 𝜉. The case result forgets that tail because both branches return 𝖭𝖺𝗍, but the injection’s formation constraint 𝜉\ℓ remains in the normalized predicate set.