Exercise 16.1.
The monomorphic choice fixes the available module at the definition site: 𝐴.𝑓⇝𝜆𝑥:𝖨𝗇𝗍.𝖤𝗊𝖨𝗇𝗍.𝖾𝗊(𝑥,𝑥+2). Hence 𝐵.𝑦⇝𝖤𝗊𝖨𝗇𝗍.𝖾𝗊(3,5)=𝖿𝖺𝗅𝗌𝖾. The constrained-polymorphic choice exports the evidence parameter: 𝐴.𝑓⇝Λ𝑋:𝖤𝖰.𝜆𝑥:𝑋.𝑡.𝑋.𝖾𝗊(𝑥,𝑥+2). At the use in 𝐵, inference may instantiate 𝑋 with 𝖤𝗊𝖯𝖺𝗋𝗂𝗍𝗒, giving 𝐵.𝑦⇝𝖤𝗊𝖯𝖺𝗋𝗂𝗍𝗒.𝖾𝗊(3,5)=𝗍𝗋𝗎𝖾. The boundary of 𝐴 must decide whether 𝑓 is exported at the monomorphic signature 𝖨𝗇𝗍 →𝖡𝗈𝗈𝗅 or at the constrained-polymorphic signature ∀𝑋 :𝖤𝖰.𝑋.𝑡 →𝖡𝗈𝗈𝗅. Instance scope alone cannot determine that exported interface.
Exercise 16.2.
The evidence tree is 𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍⟨𝖲𝗁𝗈𝗐𝖯𝖺𝗂𝗋⟨𝖲𝗁𝗈𝗐𝖡𝗈𝗈𝗅,𝖲𝗁𝗈𝗐𝖨𝗇𝗍⟩⟩. Its proper evidence subterms have types 𝖲𝗁𝗈𝗐𝖡𝗈𝗈𝗅:𝖲𝖧𝖮𝖶[𝖡𝗈𝗈𝗅],𝖲𝗁𝗈𝗐𝖨𝗇𝗍:𝖲𝖧𝖮𝖶[𝖨𝗇𝗍],𝖲𝗁𝗈𝗐𝖯𝖺𝗂𝗋⟨𝖲𝗁𝗈𝗐𝖡𝗈𝗈𝗅,𝖲𝗁𝗈𝗐𝖨𝗇𝗍⟩:𝖲𝖧𝖮𝖶[𝖯𝖺𝗂𝗋(𝖡𝗈𝗈𝗅,𝖨𝗇𝗍)]. Applying 𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍 to the last subterm gives 𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍⟨𝖲𝗁𝗈𝗐𝖯𝖺𝗂𝗋⟨𝖲𝗁𝗈𝗐𝖡𝗈𝗈𝗅,𝖲𝗁𝗈𝗐𝖨𝗇𝗍⟩⟩:𝖲𝖧𝖮𝖶[𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝖡𝗈𝗈𝗅,𝖨𝗇𝗍))].
Exercise 16.3.
The two accepted declarations have result heads (𝖲𝖧𝖮𝖶,𝖨𝗇𝗍)and(𝖲𝖧𝖮𝖶,𝖫𝗂𝗌𝗍). Both heads are absent from the original environment, so lemma 16.4 applies to 𝖲𝗁𝗈𝗐𝖨𝗇𝗍 and to 𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍.
For 𝖤𝗊𝖭𝖾𝗏𝖾𝗋, the result head is (𝖤𝖰,𝖨𝗇𝗍), already occupied by 𝖤𝗊𝖨𝗇𝗍. The non-overlap premise fails. If the adoption were allowed, the same request would have two derivations, Θ⊢𝖤𝖰[𝖨𝗇𝗍]⇓𝗋𝖾𝗌𝖤𝗊𝖨𝗇𝗍andΘ⊢𝖤𝖰[𝖨𝗇𝗍]⇓𝗋𝖾𝗌𝖤𝗊𝖭𝖾𝗏𝖾𝗋, with observably different equality operations.
Exercise 16.4.
Depth-first, left-to-right resolution visits these keys: (𝖤𝖰,𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝖨𝗇𝗍,𝖡𝗈𝗈𝗅))),(𝖤𝖰,𝖯𝖺𝗂𝗋(𝖨𝗇𝗍,𝖡𝗈𝗈𝗅)),(𝖤𝖰,𝖨𝗇𝗍),(𝖤𝖰,𝖡𝗈𝗈𝗅). The first three lookups find 𝖤𝗊𝖫𝗂𝗌𝗍, 𝖤𝗊𝖯𝖺𝗂𝗋, and 𝖤𝗊𝖨𝗇𝗍. The fourth finds no declaration, so the first reported error is 𝗆𝗂𝗌𝗌𝗂𝗇𝗀(𝖤𝖰,𝖡𝗈𝗈𝗅). There is no possible R-Base conclusion for the Boolean request. Thus the second premise needed for the R-Functor application of 𝖤𝗊𝖯𝖺𝗂𝗋 cannot be completed; the enclosing 𝖤𝗊𝖫𝗂𝗌𝗍 premise consequently fails as well.
Exercise 16.5.
Put 𝑉:=𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍⟨𝖲𝗁𝗈𝗐𝖯𝖺𝗂𝗋⟨𝖲𝗁𝗈𝗐𝖡𝗈𝗈𝗅,𝖲𝗁𝗈𝗐𝖨𝗇𝗍⟩⟩. Resolution gives Θ⊢𝖲𝖧𝖮𝖶[𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝖡𝗈𝗈𝗅,𝖨𝗇𝗍))]⇓𝗋𝖾𝗌𝑉. The source elaborates to 𝜆𝑧:𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝖡𝗈𝗈𝗅,𝖨𝗇𝗍)).𝑉.𝗌𝗁𝗈𝗐 𝑧. Let Γ𝑧:=𝑧:𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝖡𝗈𝗈𝗅,𝖨𝗇𝗍)). Evidence typing gives Θ⊢𝑉:𝖲𝖧𝖮𝖶[𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝖡𝗈𝗈𝗅,𝖨𝗇𝗍))]. Target weakening therefore gives the same judgment under Γ𝑧;Θ, and signature projection gives Γ𝑧;Θ⊢𝑉.𝗌𝗁𝗈𝗐:𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝖡𝗈𝗈𝗅,𝖨𝗇𝗍))→𝖲𝗍𝗋𝗂𝗇𝗀. The variable rule types 𝑧 in Γ𝑧;Θ, so application has type 𝖲𝗍𝗋𝗂𝗇𝗀. Abstraction discharges 𝑧 and gives the claimed function type.
Admissibility supplies the three hypotheses used by theorem 16.6. Well-typed declarations establish evidence typing. Decrease establishes termination, and non-overlap makes the selected evidence unique. The target derivation itself needs only the returned evidence’s realized signature.
Exercise 16.6.
The outer list request selects 𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍. Its premise is the pair request, which selects 𝖲𝗁𝗈𝗐𝖯𝖺𝗂𝗋. The first pair premise is the variable request 𝖲𝖧𝖮𝖶[𝛼], so retain 𝑋:𝖲𝖧𝖮𝖶[𝛼]. The second premise is a list request at 𝖨𝗇𝗍, solved by 𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍⟨𝖲𝗁𝗈𝗐𝖨𝗇𝗍⟩. Reduction therefore returns (𝑋:𝖲𝖧𝖮𝖶[𝛼],𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍⟨𝖲𝗁𝗈𝗐𝖯𝖺𝗂𝗋⟨𝑋,𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍⟨𝖲𝗁𝗈𝗐𝖨𝗇𝗍⟩⟩⟩). Grounding 𝛼 to 𝖡𝗈𝗈𝗅 and substituting 𝖲𝗁𝗈𝗐𝖡𝗈𝗈𝗅 for 𝑋 gives 𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍⟨𝖲𝗁𝗈𝗐𝖯𝖺𝗂𝗋⟨𝖲𝗁𝗈𝗐𝖡𝗈𝗈𝗅,𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍⟨𝖲𝗁𝗈𝗐𝖨𝗇𝗍⟩⟩⟩, which has the realized signature 𝖲𝖧𝖮𝖶[𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝖡𝗈𝗈𝗅,𝖫𝗂𝗌𝗍(𝖨𝗇𝗍)))].
Exercise 16.7.
Yes. This is the theorem’s ground top-level consequence.
No. This is a completeness claim. The published theorem is one-way soundness, and the calculus intentionally inherits incompleteness from ML module inference.
No. This is a coherence claim comparing successful elaborations. Type preservation of each elaboration does not establish their observational equivalence.
Yes. Future-world extension and a canonical solution of the residual constraints are explicit hypotheses of the soundness theorem, and the term clause concludes declarative typing after those substitutions.
Exercise 16.8.
The explicit argument and expected result constrain the missing module 𝑀 by 𝑀.𝑡=𝖨𝗇𝗍 𝗅𝗂𝗌𝗍. Trying either list functor removes one list constructor and generates the argument constraint 𝑆.𝑡 =𝖨𝗇𝗍, solved by 𝖲𝗁𝗈𝗐𝖨𝗇𝗍. The two complete candidates are therefore 𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍(𝖲𝗁𝗈𝗐𝖨𝗇𝗍)and𝖢𝗈𝗆𝗉𝖺𝖼𝗍𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍(𝖲𝗁𝗈𝗐𝖨𝗇𝗍). Repeated use of either functor would be decreasing because its argument constraint is structurally smaller than its result constraint. Hence the termination check succeeds. Uniqueness fails because the two module expressions are distinct and implement different printing operations. The call is rejected as ambiguous.
Exercise 16.9.
Name the outer implicit integer 𝑖10, the fresh parameter introduced while checking 𝑎𝑑𝑑 by 𝑖𝑎, and the inner implicit integer by 𝑖3. Checking the declared implicit-function type introduces 𝑖𝑎 before checking the body, so the query in 𝑥 +? selects that rightmost eligible variable: 𝑎𝑑𝑑⇝𝜆𝑖𝑎:𝖨𝗇𝗍.𝜆𝑥:𝖨𝗇𝗍.𝑥+𝑖𝑎. The later inner let is not in lexical scope at this definition and cannot be observed by that query.
At the use of 𝑎𝑑𝑑, automatic implicit elimination generates a new query at 𝖨𝗇𝗍. Here 𝑖3 is the rightmost eligible binding, so 𝑎𝑑𝑑 4⇝(𝜆𝑖𝑎.𝜆𝑥.𝑥+𝑖𝑎) 𝑖3 4⟶∗4+𝑖3. Substituting the inner binding yields 4 +3 =7. The outer value 10 is shadowed at the call site.
Exercise 16.10.
Take 𝖨𝗇𝗍𝖫𝗂𝗌𝗍𝗌:𝖢𝖮𝖫𝖫𝖤𝖢𝖳𝖨𝖮𝖭[𝑡=𝖫𝗂𝗌𝗍(𝖨𝗇𝗍),𝖤𝗅𝖾𝗆=𝖨𝗇𝗍],𝖨𝗇𝗍𝖲𝖾𝗍𝗌:𝖢𝖮𝖫𝖫𝖤𝖢𝖳𝖨𝖮𝖭[𝑡=𝖨𝗇𝗍𝖲𝖾𝗍,𝖤𝗅𝖾𝗆=𝖨𝗇𝗍]. The first module may define 𝖾𝗆𝗉𝗍𝗒 =[] and 𝗂𝗇𝗌𝖾𝗋𝗍 =𝖼𝗈𝗇𝗌; the second may define the empty finite set and its duplicate-removing insertion operation. Their realized signatures are 𝖨𝗇𝗍𝖫𝗂𝗌𝗍𝗌:𝗌𝗂𝗀 {𝗍𝗒𝗉𝖾 𝑡=𝖫𝗂𝗌𝗍(𝖨𝗇𝗍);𝗍𝗒𝗉𝖾 𝖤𝗅𝖾𝗆=𝖨𝗇𝗍;𝗏𝖺𝗅 𝖾𝗆𝗉𝗍𝗒:𝑡;𝗏𝖺𝗅 𝗂𝗇𝗌𝖾𝗋𝗍:𝖤𝗅𝖾𝗆→𝑡→𝑡}, and 𝖨𝗇𝗍𝖲𝖾𝗍𝗌:𝗌𝗂𝗀 {𝗍𝗒𝗉𝖾 𝑡=𝖨𝗇𝗍𝖲𝖾𝗍;𝗍𝗒𝗉𝖾 𝖤𝗅𝖾𝗆=𝖨𝗇𝗍;𝗏𝖺𝗅 𝖾𝗆𝗉𝗍𝗒:𝑡;𝗏𝖺𝗅 𝗂𝗇𝗌𝖾𝗋𝗍:𝖤𝗅𝖾𝗆→𝑡→𝑡}. Consequently both 𝖨𝗇𝗍𝖫𝗂𝗌𝗍𝗌.𝗂𝗇𝗌𝖾𝗋𝗍 3 𝖨𝗇𝗍𝖫𝗂𝗌𝗍𝗌.𝖾𝗆𝗉𝗍𝗒and𝖨𝗇𝗍𝖲𝖾𝗍𝗌.𝗂𝗇𝗌𝖾𝗋𝗍 3 𝖨𝗇𝗍𝖲𝖾𝗍𝗌.𝖾𝗆𝗉𝗍𝗒 are well typed at their different carrier types. The equality dictionary 𝑡 →𝑡 →𝖡𝗈𝗈𝗅 contains neither an associated element type nor the 𝖾𝗆𝗉𝗍𝗒 and 𝗂𝗇𝗌𝖾𝗋𝗍 operations. Replacing the module by that single function would discard precisely the static information this class is meant to expose.
Exercise 16.11.
The smallest counterexample is Θ={𝖤𝗊𝖨𝗇𝗍:𝖤𝖰[𝖨𝗇𝗍]}. It resolves integer equality to 𝖤𝗊𝖨𝗇𝗍. If an extension is allowed to replace that head with 𝖤𝗊𝖭𝖾𝗏𝖾𝗋 :𝖤𝖰[𝖨𝗇𝗍], the same request resolves to 𝖤𝗊𝖭𝖾𝗏𝖾𝗋. Stability fails already at a one-node evidence tree.
A stronger valid statement is the following. Let Θ and Θ′ be finite admissible environments, viewed as maps from result heads to declarations. Suppose that, for every head used in the derivation of Θ ⊢𝐾[𝜏] ⇓𝗋𝖾𝗌𝑉, the declaration stored at that head in Θ′ is syntactically identical to the declaration stored in Θ. Entries at all other heads may be inserted, deleted, or replaced. Then Θ′ ⊢𝐾[𝜏] ⇓𝗋𝖾𝗌𝑉, and its deterministic resolver returns 𝑉.
Prove this by induction on the derivation of 𝑉. At a base node, the identical declaration remains the lookup result. At a functor node, the identical functor remains the lookup result, and the induction hypotheses preserve every premise evidence subtree. Reapplying R-Functor reconstructs exactly 𝑉. Changes at heads outside the old tree are never consulted. Admissibility of Θ′ ensures that the deterministic resolver is defined and that no competing declaration exists at a retained head.
Exercise 16.12.
The two evidence paths are 𝖮𝗋𝖽𝖳𝗈𝖤𝗊(𝖮𝗋𝖽𝖨𝗇𝗍)and𝖧𝖺𝗌𝗁𝖳𝗈𝖤𝗊(𝖧𝖺𝗌𝗁𝖨𝗇𝗍), and both match 𝖤𝖰 with carrier 𝖨𝗇𝗍. The search is a diamond because distinct source modules and functors converge on the same requested module type.
An alias test may collapse the paths when both applications elaborate to a manifest alias of one named module, for example when their result signatures and module equations establish 𝖮𝗋𝖽𝖳𝗈𝖤𝗊(𝖮𝗋𝖽𝖨𝗇𝗍)=𝖤𝗊𝖨𝗇𝗍=𝖧𝖺𝗌𝗁𝖳𝗈𝖤𝗊(𝖧𝖺𝗌𝗁𝖨𝗇𝗍) in the module language’s decidable path-equivalence relation. Merely knowing that the two 𝖾𝗊 functions return the same booleans on all integer inputs is not such a test. Extensional equality of arbitrary functions is not decidable in a general programming language, and the modules may contain additional components invisible to that one observation.
Exercise 16.13.
Let the implicit environment contain, from left to right, 𝑖:𝖨𝗇𝗍,𝑏:𝖡𝗈𝗈𝗅,𝑦:∀𝑋.𝑋?→𝖨𝗇𝗍, and ultimately check a query at 𝖨𝗇𝗍. In both derivations, SI-Query first synthesizes the type of the same rightmost implicit variable 𝑦, so lexical well-scopedness does not distinguish them. SI-AllE can nevertheless choose 𝑋 =𝖨𝗇𝗍, after which SI-ImpE supplies 𝑖 and SI-Stitch reaches the checking judgment, yielding 𝑦[𝖨𝗇𝗍] 𝑖:𝖨𝗇𝗍, or SI-AllE can choose 𝑋 =𝖡𝗈𝗈𝗅, after which SI-ImpE supplies 𝑏, yielding 𝑦[𝖡𝗈𝗈𝗅] 𝑏:𝖨𝗇𝗍. These are distinct elaborations of the same query.
The monomorphic uniqueness induction relies on the synthesized restricted type of a selected term determining the chain of implicit eliminations. The nondeterministic ∀-elimination step now inserts an arbitrary type before that chain, so the induction no longer determines one premise derivation. This is failure of uniqueness even when both derivations are finite. Divergence is a separate algorithmic possibility caused by recursive implicit search; it is not needed for this counterexample.
Exercise 16.14.
Use modular implicits. Put each pretty-printer module in the lexical implicit search space of the call sites where it is intended to be unique. The search key is the constrained 𝖲𝖧𝖮𝖶 module type.
Use modular type classes, or equivalently explicit modules when omission is unnecessary. A module signature can carry an associated abstract output type and preserve it across an abstraction boundary.
Use SI implicit functions. The tolerance is an ordinary value selected by expected type and the rightmost lexical binding, exactly the required shadowing behavior.
Use explicit module passing. The dependency is visible at every call, and there is no candidate search whose failure, overlap, or environmental change could alter selection.
Use 𝖬𝖳𝖢0 or the full modular-type-class discipline. The pair (𝖤𝖰,𝑐) selects one total constructor functor, and recursive requests follow the nested type structure.
For item 1, SI is a plausible alternative: one could pass an implicit value of type 𝑡 →𝖲𝗍𝗋𝗂𝗇𝗀. It is worse when the printer is naturally a module with associated types or auxiliary operations, because type-only value search loses that module interface and can collide with unrelated implicit functions of the same ordinary type.