Exercise 4.1.
Abbreviate the three field types by 𝐻 =𝖲𝗍𝗋𝗂𝗇𝗀, 𝑁 =𝖭𝖺𝗍, and 𝐵 =𝖡𝗈𝗈𝗅. The original row already has 𝗁𝗈𝗌𝗍 first: {𝗁𝗈𝗌𝗍:𝐻,𝗉𝗈𝗋𝗍:𝑁,𝗌𝖾𝖼𝗎𝗋𝖾:𝐵∣𝜉}. One adjacent exchange puts 𝗉𝗈𝗋𝗍 first: {𝗁𝗈𝗌𝗍:𝐻,𝗉𝗈𝗋𝗍:𝑁,𝗌𝖾𝖼𝗎𝗋𝖾:𝐵∣𝜉}=𝗋{𝗉𝗈𝗋𝗍:𝑁,𝗁𝗈𝗌𝗍:𝐻,𝗌𝖾𝖼𝗎𝗋𝖾:𝐵∣𝜉}. To put 𝗌𝖾𝖼𝗎𝗋𝖾 first, move it left twice: {𝗁𝗈𝗌𝗍:𝐻,𝗉𝗈𝗋𝗍:𝑁,𝗌𝖾𝖼𝗎𝗋𝖾:𝐵∣𝜉}=𝗋{𝗁𝗈𝗌𝗍:𝐻,𝗌𝖾𝖼𝗎𝗋𝖾:𝐵,𝗉𝗈𝗋𝗍:𝑁∣𝜉}=𝗋{𝗌𝖾𝖼𝗎𝗋𝖾:𝐵,𝗁𝗈𝗌𝗍:𝐻,𝗉𝗈𝗋𝗍:𝑁∣𝜉}. Every exchange is between distinct labels. The assumptions that 𝜉 lacks all three labels rebuild the Row-Ext premises on both sides of each exchange, so these raw equalities are equalities under 𝑃.
If the last label is another 𝗉𝗈𝗋𝗍, formation already fails. After forming the inner row {𝗉𝗈𝗋𝗍 :𝑁 ∣𝜉}, adding the outer 𝗉𝗈𝗋𝗍 would require 𝑃⊩{𝗉𝗈𝗋𝗍:𝑁∣𝜉}\𝗉𝗈𝗋𝗍. Predicate simplification reaches ⊥ at the displayed matching head. Consequently there is no strict row on which the proposed exchange calculation could begin.
Exercise 4.2.
Let 𝑒:𝛼,𝑟:𝖱𝖾𝖼({ℓ:𝛽∣𝜉}),𝑃={𝜉\ℓ}. The update abbreviation expands as {ℓ:=𝑒∣𝑟}={ℓ=𝑒∣𝑟−ℓ}. Rule Q-Restrict removes the exposed field and preserves the single tail: 𝑟−ℓ:𝖱𝖾𝖼(𝜉). Rule Q-Extend then combines 𝑒 :𝛼 with that residual record: {ℓ=𝑒∣𝑟−ℓ}:𝖱𝖾𝖼({ℓ:𝛼∣𝜉}). Its only new premise is 𝑃 ⊩𝜉\ℓ. The same predicate is also exactly the premise that forms the input row {ℓ :𝛽 ∣𝜉} and the output row {ℓ :𝛼 ∣𝜉}.
Abstracting the two term variables and generalizing gives ∀𝛼∀𝛽∀𝜉.(𝜉\ℓ)⇒𝛼→𝖱𝖾𝖼({ℓ:𝛽∣𝜉})→𝖱𝖾𝖼({ℓ:𝛼∣𝜉}). The same 𝜉 at all three stages expresses preservation of every untouched field.
Exercise 4.3.
Put 𝜌={𝖾𝗋𝗋𝗈𝗋:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜉}. The assumptions on 𝜉 imply 𝑃 ⊩𝜌\𝗈𝗄, because 𝖾𝗋𝗋𝗈𝗋 ≠𝗈𝗄. The required term is 𝖼𝖺𝗌𝖾𝗈𝗄 𝑒 𝗈𝖿 ⎧{
{⎨{
{⎩⟨𝗈𝗄=𝑥⟩↦⟨𝗈𝗄=𝑓𝑥⟩,𝑦↦𝖾𝗆𝖻𝖾𝖽𝗈𝗄𝑦. The scrutinee has type 𝖵𝖺𝗋({𝗈𝗄 :𝛼 ∣𝜌}). In the matching branch, 𝑥 :𝛼, hence 𝑓 𝑥 :𝛽. Choose residual row 𝜌 in Q-Inject; its lacks premise gives ⟨𝗈𝗄=𝑓𝑥⟩:𝖵𝖺𝗋({𝗈𝗄:𝛽∣𝜌}). In the residual branch, 𝑦 :𝖵𝖺𝗋(𝜌). Rule Q-Embed chooses the fresh alternative type to be 𝛽 and gives 𝖾𝗆𝖻𝖾𝖽𝗈𝗄𝑦:𝖵𝖺𝗋({𝗈𝗄:𝛽∣𝜌}). Both branches therefore have the same result type. Expanding 𝜌 gives exactly 𝖵𝖺𝗋({𝗈𝗄:𝛽,𝖾𝗋𝗋𝗈𝗋:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜉}). Only the 𝗈𝗄 payload passes through 𝑓; every other tag enters the residual branch and is embedded unchanged.
Exercise 4.4.
Suppose the lookup has been typed, after any final row conversion, as ⋅⊢𝑞{𝑚=𝑤∣𝑅}.ℓ:∅⇒𝜏. Inverting Q-Select gives a closed strict row 𝜌 with ⋅⊢𝑞{𝑚=𝑤∣𝑅}:∅⇒𝖱𝖾𝖼(𝜌),𝜌≡{ℓ:𝜏∣𝜌ℓ}. Peeling the record value at its run-time head gives a type 𝜐 and a row 𝜌0 such that 𝜌≡{𝑚:𝜐∣𝜌0},⋅⊢𝑞𝑤:∅⇒𝜐,⋅⊢𝑞𝑅:∅⇒𝖱𝖾𝖼(𝜌0). Thus 𝜌0 ≡𝜌 −𝑚.
Because 𝑚 ≠ℓ, deleting 𝑚 does not remove the ℓ field. Deletion commutation gives (𝜌−𝑚)−ℓ≡(𝜌−ℓ)−𝑚. Equivalently, the finite-map characterization exposes 𝜌0≡{ℓ:𝜏∣(𝜌−ℓ)−𝑚}. Convert the typing of 𝑅 along this equality and apply Q-Select: ⋅⊢𝑞𝑅.ℓ:∅⇒𝜏. This is exactly the type of the source redex, so the primitive reduction {𝑚=𝑤∣𝑅}.ℓ⟶𝑅.ℓ preserves 𝜏.
exercise 4.5.
Write the displayed rows with their tails exposed. Their formation needs 𝑃0={𝜉\𝗑,𝜉\𝗒,𝜁\𝗑,𝜁\𝗓}. Insertion first exposes the 𝗑 field on the right, passing the distinct 𝗓 field. It unifies 𝛼 with 𝖭𝖺𝗍 and leaves {𝗒:𝖡𝗈𝗈𝗅∣𝜉}≐{𝗓:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜁}. To expose 𝗒 on the right, insertion reaches the open tail 𝜁. Choose fresh 𝜂 and put 𝜁 ={𝗒 :𝖡𝗈𝗈𝗅 ∣𝜂}, generating the strictness requirement 𝜂\𝗒. Deleting the exposed field leaves {𝗓 :𝖲𝗍𝗋𝗂𝗇𝗀 ∣𝜂}, so the final tail equation sets 𝜉 ={𝗓 :𝖲𝗍𝗋𝗂𝗇𝗀 ∣𝜂}. Thus one printed MGU is 𝑈=[𝖭𝖺𝗍/𝛼,{𝗓:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜂}/𝜉,{𝗒:𝖡𝗈𝗈𝗅∣𝜂}/𝜁], and normalization of 𝑃0[𝑈] gives 𝑄={𝜂\𝗑,𝜂\𝗒,𝜂\𝗓}. For the two additional predicates, 𝜉[𝑈]\𝗓={𝗓:𝖲𝗍𝗋𝗂𝗇𝗀∣𝜂}\𝗓⇓⊥,𝜁[𝑈]\𝗒⇓⊥. Hence the equation problem has the displayed MGU, but the problem augmented by either requested predicate has no admissible solution.
exercise 4.6.
Give 𝑥 fresh type 𝛼. Injection chooses fresh 𝜉 and returns {𝜉\𝗈𝗄},𝖵𝖺𝗋({𝗈𝗄:𝛼∣𝜉}). For embedding, choose a fresh type 𝛽 for the new 𝖾𝗋𝗋𝗈𝗋 alternative and a fresh row variable 𝜁 for the old row. Solving 𝖵𝖺𝗋({𝗈𝗄:𝛼∣𝜉})≐𝖵𝖺𝗋(𝜁) sets 𝜁 ={𝗈𝗄 :𝛼 ∣𝜉}. Its new requirement 𝜁\𝖾𝗋𝗋𝗈𝗋 normalizes to 𝜉\𝖾𝗋𝗋𝗈𝗋. Lambda formation and generalization give ∀𝛼𝛽𝜉.(𝜉\𝗈𝗄,𝜉\𝖾𝗋𝗋𝗈𝗋)⇒𝛼→𝖵𝖺𝗋({𝖾𝗋𝗋𝗈𝗋:𝛽,𝗈𝗄:𝛼∣𝜉}). If 𝜉 contained 𝗈𝗄, injection would duplicate its selected alternative; if it contained 𝖾𝗋𝗋𝗈𝗋, embedding would duplicate the newly added alternative. The two residual predicates are therefore independent and both necessary.
exercise 4.7.
In the first row, only 𝗁𝗈𝗌𝗍 is smaller than 𝗈𝗄, so the offset is 1. In the second, only 𝖾𝗋𝗋𝗈𝗋 is smaller than 𝗁𝗈𝗌𝗍, so that offset is also 1. These counts are unchanged if the two fields are displayed in the opposite syntactic order.
An 𝗈𝗄 injection into the one-alternative row has tag 0. Embedding it past a new 𝗁𝗈𝗌𝗍 alternative inserts a smaller label, so evidence increments the tag to 1. Embedding the result past 𝖾𝗋𝗋𝗈𝗋 inserts another smaller label and increments it to 2. The payload never changes; only its position in the canonical ordered sum does.
Exercise 4.8.
Let 𝐴={𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀,𝗉𝗈𝗋𝗍:𝖭𝖺𝗍,𝗌𝖾𝖼𝗎𝗋𝖾:𝖡𝗈𝗈𝗅},𝐵={𝗉𝗈𝗋𝗍:𝖭𝖺𝗍}. Since the label set of 𝐵 is contained in that of 𝐴, Width derives 𝐴 <:𝐵. From 𝑟 :𝐴, Sub gives 𝑟 :𝐵. With 𝗉𝗈𝗋𝗍𝖮𝖿 :𝐵 →𝖭𝖺𝗍, application yields 𝗉𝗈𝗋𝗍𝖮𝖿𝑟:𝖭𝖺𝗍.
For the row-polymorphic derivation, instantiate 𝗉𝗈𝗋𝗍𝖮𝖿:∀𝛼𝜉.(𝜉\𝗉𝗈𝗋𝗍)⇒𝖱𝖾𝖼({𝗉𝗈𝗋𝗍:𝛼∣𝜉})→𝛼 by 𝛼=𝖭𝖺𝗍,𝜉={𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀,𝗌𝖾𝖼𝗎𝗋𝖾:𝖡𝗈𝗈𝗅}. The chosen row lacks 𝗉𝗈𝗋𝗍 by two unequal-head reductions followed by L-Empty. Row permutation identifies the instantiated domain with 𝖱𝖾𝖼(𝐴), so the application again has type 𝖭𝖺𝗍.
Width subtyping may discard the 𝗁𝗈𝗌𝗍 and 𝗌𝖾𝖼𝗎𝗋𝖾 fields when it views 𝐴 as 𝐵. A fixed result type for adding 𝗍𝗂𝗆𝖾𝗈𝗎𝗍 can mention the fields retained in 𝐵, but contains no variable linked to all the fields forgotten by subsumption. It therefore cannot prove that those arbitrary fields survive. A row type can express that link: ∀𝜉.(𝜉\𝗍𝗂𝗆𝖾𝗈𝗎𝗍)⇒𝖱𝖾𝖼(𝜉)→𝖱𝖾𝖼({𝗍𝗂𝗆𝖾𝗈𝗎𝗍:𝜏∣𝜉}). The same 𝜉 in domain and codomain preserves all three input fields in this instance.
Exercise 4.10.
The principal declaration is 𝗐𝗂𝗍𝗁𝖲𝖾𝖼𝗎𝗋𝖾:∀𝜉.(𝜉\𝗌𝖾𝖼𝗎𝗋𝖾)⇒𝖱𝖾𝖼(𝜉)→𝖱𝖾𝖼({𝗌𝖾𝖼𝗎𝗋𝖾:𝖡𝗈𝗈𝗅∣𝜉}). Set 𝜉[𝐺] ={𝗁𝗈𝗌𝗍 :𝖲𝗍𝗋𝗂𝗇𝗀}. Its predicate discharges by {𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀}\𝗌𝖾𝖼𝗎𝗋𝖾⇓𝜖𝜌\𝗌𝖾𝖼𝗎𝗋𝖾⇓⊤. Ground discharge therefore gives ⋅⊢𝑞𝗐𝗂𝗍𝗁𝖲𝖾𝖼𝗎𝗋𝖾:∅⇒𝖱𝖾𝖼({𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀})→𝖱𝖾𝖼({𝗌𝖾𝖼𝗎𝗋𝖾:𝖡𝗈𝗈𝗅,𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀}). The argument is typed by Q-Empty, Q-Const, and Q-Extend: ⋅⊢𝑞{𝗁𝗈𝗌𝗍="𝚍𝚋"∣{}}:∅⇒𝖱𝖾𝖼({𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀}). Application is therefore ground typed at the displayed result record type. Its reduction is (𝜆𝑟.{𝗌𝖾𝖼𝗎𝗋𝖾=𝗍𝗋𝗎𝖾∣𝑟}){𝗁𝗈𝗌𝗍="𝚍𝚋"∣{}}⟶{𝗌𝖾𝖼𝗎𝗋𝖾=𝗍𝗋𝗎𝖾∣{𝗁𝗈𝗌𝗍="𝚍𝚋"∣{}}}.
Now try a ground row containing 𝗌𝖾𝖼𝗎𝗋𝖾 :𝖭𝖺𝗍, for example 𝜉[𝐺′]={𝗁𝗈𝗌𝗍:𝖲𝗍𝗋𝗂𝗇𝗀,𝗌𝖾𝖼𝗎𝗋𝖾:𝖭𝖺𝗍}. Normalization of the required predicate first passes the unequal 𝗁𝗈𝗌𝗍 head and then reaches the matching one: 𝜉[𝐺′]\𝗌𝖾𝖼𝗎𝗋𝖾⇓{𝗌𝖾𝖼𝗎𝗋𝖾:𝖭𝖺𝗍}\𝗌𝖾𝖼𝗎𝗋𝖾⇓⊥. Hence 𝐺′ is not admissible and there is no corresponding ground instantiation or application typing.
exercise 4.11.
Because (𝑉,𝑄𝑉) is a solution of the original problem, principality of (𝑈,𝑄𝑈) supplies an admissible 𝑅 :𝑄𝑈 →𝑄𝑉 such that 𝑉 ≡𝑄𝑉𝑈;𝑅 on every problem variable. Interchanging the two solutions supplies an admissible 𝑆 :𝑄𝑉 →𝑄𝑈 with 𝑈 ≡𝑄𝑈𝑉;𝑆. Literal equality is neither needed nor generally true: row permutation and the names chosen for fresh tails are invisible to the equation problem.
For example, solve {𝗑:𝖭𝖺𝗍∣𝜉}≐{𝗒:𝖡𝗈𝗈𝗅∣𝜁} under the two input strictness predicates. One run gives 𝜉[𝑈]={𝗒:𝖡𝗈𝗈𝗅∣𝜔},𝜁[𝑈]={𝗑:𝖭𝖺𝗍∣𝜔},𝑄𝑈={𝜔\𝗑,𝜔\𝗒}. Applying 𝑈 to the two original rows displays the same finite map in different orders: {𝗑:𝖭𝖺𝗍,𝗒:𝖡𝗈𝗈𝗅∣𝜔}≡𝑄𝑈{𝗒:𝖡𝗈𝗈𝗅,𝗑:𝖭𝖺𝗍∣𝜔}. The adjacent exchange is legal because 𝗑 ≠𝗒; the two predicates in 𝑄𝑈 form both displays strictly. A run that chooses 𝜈 instead gives the analogous (𝑉,𝑄𝑉); it may also print the two substitutions and predicates in the opposite order. Define 𝜔[𝑅] =𝜈 and 𝜈[𝑆] =𝜔, acting identically elsewhere. Both are admissible, and the two displayed factorization equations hold modulo their target predicate contexts. Thus the fresh names and print orders differ while the principal solution does not.
Exercise 4.9.
Give the new values types 𝛼 and 𝛽, the old ℓ and 𝑚 fields types 𝛾 and 𝛿, and let the remaining row be 𝜉. Take 𝑃={𝜉\ℓ,𝜉\𝑚},ℓ≠𝑚. The principal scheme is ∀𝛼𝛽𝛾𝛿𝜉.𝑃⇒𝛼→𝛽→𝖱𝖾𝖼({ℓ:𝛾,𝑚:𝛿∣𝜉})→𝖱𝖾𝖼({ℓ:𝛼,𝑚:𝛽∣𝜉}).
For the inner update, first permute the input to expose 𝑚: 𝖱𝖾𝖼({ℓ:𝛾,𝑚:𝛿∣𝜉})≡𝖱𝖾𝖼({𝑚:𝛿,ℓ:𝛾∣𝜉}). Restriction and extension calculate {𝑚:=𝑏∣𝑟}={𝑚=𝑏∣𝑟−𝑚},𝑟−𝑚:𝖱𝖾𝖼({ℓ:𝛾∣𝜉}),{𝑚=𝑏∣𝑟−𝑚}:𝖱𝖾𝖼({𝑚:𝛽,ℓ:𝛾∣𝜉}). The extension premise reduces to 𝑃 ⊩{ℓ :𝛾 ∣𝜉}\𝑚, which follows from 𝜉\𝑚 and ℓ ≠𝑚.
For the outer update, permute the intermediate row to expose ℓ: {𝑚:𝛽,ℓ:𝛾∣𝜉}≡{ℓ:𝛾,𝑚:𝛽∣𝜉}. Then ({𝑚:=𝑏∣𝑟})−ℓ:𝖱𝖾𝖼({𝑚:𝛽∣𝜉}),{ℓ=𝑎∣({𝑚:=𝑏∣𝑟})−ℓ}:𝖱𝖾𝖼({ℓ:𝛼,𝑚:𝛽∣𝜉}). This extension uses the other predicate, 𝜉\ℓ.
Reversing the updates produces the displayed result row {𝑚 :𝛽,ℓ :𝛼 ∣𝜉}, which is equal to the former result by one adjacent exchange. Operationally each restriction removes one distinct field and each extension restores it with its new value. Since ℓ ≠𝑚, the two operations affect disjoint map entries; the final finite maps are identical even if the record spines display the fields in different orders.
Exercise 4.12.
In the scoped-duplicate record fragment, row and term formation admit repeated labels, record values retain repeated fields, and the operational rules for selection and restriction stop at the first displayed matching field. Equality may exchange adjacent distinct labels, but it never exchanges two equal labels. Consequently the relative order of the occurrences of any one label is invariant.
The sentence in the original canonical-forms proof that fails is the claim that the satisfied lacks predicate makes the new label absent and hence that extension creates exactly the finite map of the extended row. There is no lacks premise in the new calculus, and a value may contain several entries with the same label, so it is not a finite map from labels to values.
The repaired record canonical-form statement is this: if a closed value has a scoped-duplicate record row, then for each label it contains exactly the same ordered sequence of occurrences as the row. The payloads at those occurrences have the corresponding field types. Selection returns the payload at the first occurrence, and restriction removes that occurrence while preserving the order and types of all later occurrences. The proof is induction on the displayed record spine. Extension adds one leading occurrence; an exchange of distinct labels changes no same-label sequence.
This statement does not by itself justify an update notation containing an unqualified 𝜌 −ℓ. When 𝜌 contains several ℓ occurrences, deleting the first, the last, or another occurrence gives different residual rows, often with different next selected types; equal labels cannot be exchanged to identify those results. The update equation becomes sound only after restriction is specified to remove the first displayed occurrence. Then extension installs the replacement as the new first occurrence and leaves all later occurrences available. No claim about variants follows: their run-time tags would additionally need to record the nesting level of equal labels.
Exercise 4.13.
Every ground instance of ∀𝜉.(𝜉\ℓ)⇒𝖭𝖺𝗍 has result type 𝖭𝖺𝗍. Its evidence argument nevertheless depends on the chosen row. If 𝜉 =𝜖𝜌, the canonical offset is 0. If 𝜉 ={𝑚 :𝐴} with 𝑚 <ℓ, the canonical offset is 1. Thus the target scheme abstraction is applied to certificates describing different layouts, even though neither layout variable occurs in the visible result type.
For an open judgment the usual condition is ftv(𝑃)⊆ftv(Γ,𝜏); for a closed scheme it is ftv(𝑃)⊆ftv(𝜏). The displayed scheme violates the second condition because 𝜉 occurs in 𝑃 but not in 𝖭𝖺𝗍.
Run W on the case term. Injection first returns 𝑃0={𝜉0\ℓ},𝑆0=id,𝜏0=𝖵𝖺𝗋({ℓ:𝖭𝖺𝗍∣𝜉0}), with fresh 𝜉0. The case clause chooses fresh 𝛼,𝜉1 and solves 𝖵𝖺𝗋({ℓ:𝖭𝖺𝗍∣𝜉0})≐𝖵𝖺𝗋({ℓ:𝛼∣𝜉1}). Its principal solution follows the solver’s fixed left-variable orientation: 𝛼 =𝖭𝖺𝗍 and 𝜉0 =𝜉1, retaining 𝑄0 ={𝜉1\ℓ}. The matching branch has 𝑥 :𝖭𝖺𝗍 and returns 𝗓𝖾𝗋𝗈 :𝖭𝖺𝗍. The residual branch has 𝑦 :𝖵𝖺𝗋(𝜉1) and returns the same type. The final branch equation is the reflexive equation 𝖭𝖺𝗍 ≐𝖭𝖺𝗍, so W returns ({𝜉1\ℓ},𝑈0,𝖭𝖺𝗍). After closed generalization, its scheme is precisely of the ambiguous form above.
One repair is to reject a principal inference result when definition 7.53 fails; this is the policy selected by the main evidence-passing compiler. A different language could default every hidden row to a documented ground row, for example 𝜖𝜌, and consequently pass offset 0. That choice is deterministic but discards the ambiguous row polymorphism, so it must be part of the language specification rather than an unstated elaboration step.
Practical route.
The row solver of exercise 7.12 is built in appendix F; its success replay and rejected boundary cases are frozen in appendix E.