Prerequisites. Direct starred prerequisites: Chapter 11, Chapter 12. No later core chapter depends on this route.
Suppose integer equality is available in two useful forms. The first compares integers literally; the second always returns false. Write their module evidence as 𝖤𝗊𝖨𝗇𝗍 and 𝖤𝗊𝖭𝖾𝗏𝖾𝗋. Now consider the surface program 𝗆𝗈𝖽𝗎𝗅𝖾𝐴=𝗎𝗌𝗂𝗇𝗀𝖤𝗊𝖨𝗇𝗍𝗂𝗇𝗌𝗍𝗋𝗎𝖼𝗍𝗅𝖾𝗍𝑓𝑥=𝖾𝗊(𝑥,𝑥)𝖾𝗇𝖽,𝗆𝗈𝖽𝗎𝗅𝖾𝐵=𝗎𝗌𝗂𝗇𝗀𝖤𝗊𝖭𝖾𝗏𝖾𝗋𝗂𝗇𝗌𝗍𝗋𝗎𝖼𝗍𝗅𝖾𝗍𝑦=𝐴.𝑓3𝖾𝗇𝖽. There are two plausible elaborations. If the definition of 𝐴.𝑓 fixes the instance available at its definition, then 𝐴.𝑓⇝𝜆𝑥:𝖨𝗇𝗍.𝖤𝗊𝖨𝗇𝗍.𝖾𝗊(𝑥,𝑥),𝐵.𝑦=𝗍𝗋𝗎𝖾. If generalization instead exports a class constraint, then 𝐴.𝑓⇝Λ(𝑋:𝖤𝖰).𝜆(𝑥:𝑋.𝑡).𝑋.𝖾𝗊(𝑥,𝑥), and the call in 𝐵 may instantiate 𝑋 with 𝖤𝗊𝖭𝖾𝗏𝖾𝗋, giving 𝐵.𝑦=𝖿𝖺𝗅𝗌𝖾. The two legal elaborations are observably different: the same surface binding has changed from a monomorphic value to an implicitly parameterized one. Local instance scope, polymorphic generalization, and module abstraction cannot be allowed to settle this question independently.
The parenthesized binders above are distinct: 𝑋 is a module parameter classified by 𝖤𝖰, while 𝑥 is a term parameter whose type is the selected component 𝑋.𝑡.
Explicit module passing avoids the ambiguity. Define 𝗌𝖺𝗆𝖾(𝐸:𝖤𝖰[𝖨𝗇𝗍])𝑥𝑦:=𝐸.𝖾𝗊𝑥𝑦 and write 𝗌𝖺𝗆𝖾𝖤𝗊𝖨𝗇𝗍33. This places an evidence argument at every use. The design problem is to retain explicit modular configuration while reconstructing omitted module evidence predictably.
★★☆ Let 𝖤𝗊𝖯𝖺𝗋𝗂𝗍𝗒.𝖾𝗊(𝑚,𝑛) hold exactly when 𝑚 and 𝑛 have the same parity. Replace 𝖤𝗊𝖭𝖾𝗏𝖾𝗋 by 𝖤𝗊𝖯𝖺𝗋𝗂𝗍𝗒, replace the body of 𝑓 by 𝖾𝗊(𝑥,𝑥+2), and let 𝐵 evaluate 𝐴.𝑓3. Write the two target terms for 𝐴.𝑓, reduce the resulting value of 𝐵.𝑦 in each elaboration, and identify the single source-language decision that must be fixed at the boundary of 𝐴.
Ground types are finite constructor trees 𝜏::=𝑐(𝜏1,…,𝜏𝑛), where a nullary constructor is written without parentheses. Thus 𝖨𝗇𝗍, 𝖡𝗈𝗈𝗅, 𝖫𝗂𝗌𝗍(𝖨𝗇𝗍), and 𝖯𝖺𝗂𝗋(𝖨𝗇𝗍,𝖡𝗈𝗈𝗅) are ground types. Their size is |𝑐(𝜏1,…,𝜏𝑛)|:=1+𝑛∑𝑖=1|𝜏𝑖|.
An atomic class signature has one distinguished type component 𝑡 and finitely many value components whose types may mention 𝑡. Its realization at 𝜏, written 𝐾[𝜏], makes the equation 𝑡=𝜏 transparent. The running signatures are 𝖤𝖰:=𝗌𝗂𝗀{𝗍𝗒𝗉𝖾𝑡;𝗏𝖺𝗅𝖾𝗊:𝑡→𝑡→𝖡𝗈𝗈𝗅},𝖲𝖧𝖮𝖶:=𝗌𝗂𝗀{𝗍𝗒𝗉𝖾𝑡;𝗏𝖺𝗅𝗌𝗁𝗈𝗐:𝑡→𝖲𝗍𝗋𝗂𝗇𝗀}. A module 𝑉:𝐾[𝜏] is evidence that the operations of 𝐾 are available at 𝜏.
Evidence for 𝐾[𝜏] is a module 𝑉 with a transparent component equation 𝑉.𝑡=𝜏. Resolution therefore returns module paths and applications such as 𝖤𝗊𝖫𝗂𝗌𝗍⟨𝖤𝗊𝖨𝗇𝗍⟩; uniqueness means syntactic equality of those evidence trees, not observational dictionary coherence.
For example, 𝖤𝗊𝖨𝗇𝗍:𝖤𝖰[𝖨𝗇𝗍],𝖤𝗊𝖡𝗈𝗈𝗅:𝖤𝖰[𝖡𝗈𝗈𝗅],𝖲𝗁𝗈𝗐𝖨𝗇𝗍:𝖲𝖧𝖮𝖶[𝖨𝗇𝗍],𝖲𝗁𝗈𝗐𝖡𝗈𝗈𝗅:𝖲𝖧𝖮𝖶[𝖡𝗈𝗈𝗅]. The type component matters. A module containing an integer comparison but claiming 𝑡=𝖡𝗈𝗈𝗅 is not approximately suitable evidence; it is ill typed.
Compound evidence is constructed by module functors. The equality functors for products and lists have the interfaces 𝖤𝗊𝖯𝖺𝗂𝗋:𝖤𝖰[𝛼],𝖤𝖰[𝛽]⇒𝗆𝗈𝖽𝖤𝖰[𝖯𝖺𝗂𝗋(𝛼,𝛽)],𝖤𝗊𝖫𝗂𝗌𝗍:𝖤𝖰[𝛼]⇒𝗆𝗈𝖽𝖤𝖰[𝖫𝗂𝗌𝗍(𝛼)]. Here ⇒𝗆𝗈𝖽 describes a module functor interface, not an object language implication. Applying these functors gives the evidence term 𝖤𝗊𝖫𝗂𝗌𝗍⟨𝖤𝗊𝖯𝖺𝗂𝗋⟨𝖤𝗊𝖨𝗇𝗍,𝖤𝗊𝖡𝗈𝗈𝗅⟩⟩:𝖤𝖰[𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝖨𝗇𝗍,𝖡𝗈𝗈𝗅))]. An evidence functor is total and pure. Hence resolving 𝐹⟨𝑉1,…,𝑉𝑛⟩ neither diverges nor creates a fresh abstract type; repeated applications to the same transparent evidence denote the same evidence expression.
The corresponding display functors have interfaces 𝖲𝗁𝗈𝗐𝖯𝖺𝗂𝗋:𝖲𝖧𝖮𝖶[𝛼],𝖲𝖧𝖮𝖶[𝛽]⇒𝗆𝗈𝖽𝖲𝖧𝖮𝖶[𝖯𝖺𝗂𝗋(𝛼,𝛽)],𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍:𝖲𝖧𝖮𝖶[𝛼]⇒𝗆𝗈𝖽𝖲𝖧𝖮𝖶[𝖫𝗂𝗌𝗍(𝛼)].
An instance declaration for a class 𝐾 and constructor 𝑐 of arity 𝑛 has the form 𝐹:𝐾1[𝛼𝑗1],…,𝐾𝑚[𝛼𝑗𝑚]⇒𝗆𝗈𝖽𝐾[𝑐(𝛼1,…,𝛼𝑛)], where every 𝑗𝑟 lies in {1,…,𝑛}. Its application to evidence 𝑉1,…,𝑉𝑚 is written 𝐹⟨𝑉1,…,𝑉𝑚⟩. A nullary declaration is simply a named module path 𝑃:𝐾[𝑐].
A finite availability environment Θ is admissible when:
every declaration is well typed at its displayed interface;
each premise requests evidence only for an immediate argument of the result constructor; and
for every pair (𝐾,𝑐), Θ contains at most one declaration with result head 𝐾[𝑐(…)].
The second condition is the decreasing condition; the third is nominal non-overlap.
The decreasing condition rejects 𝖤𝗊𝖠𝗀𝖺𝗂𝗇:𝖤𝖰[𝛼]⇒𝗆𝗈𝖽𝖤𝖰[𝛼]. Its result has no constructor layer that is removed by the premise. The module may be extensionally harmless, but unrestricted search can apply it forever. The condition also excludes useful instances whose premises are not structural subcomponents. It is deliberately stronger than the full calculus; a small theorem with visible hypotheses is preferable to a broader algorithm whose termination argument is left implicit.
★☆☆ Using the displayed 𝖲𝗁𝗈𝗐𝖯𝖺𝗂𝗋 and 𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍 functors, construct the complete evidence module for 𝖲𝖧𝖮𝖶[𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝖡𝗈𝗈𝗅,𝖨𝗇𝗍))], and state the type of every proper evidence subterm.
A repository may contain both 𝖤𝗊𝖨𝗇𝗍 and 𝖤𝗊𝖭𝖾𝗏𝖾𝗋. They conflict only if both are adopted for the same inference scope. We therefore distinguish a repository R of named, separately checkable modules from the finite lexical subenvironment Θ⊆R used by resolution.
An adoption is the lexical act of adding one named module from the repository to the finite subenvironment used for resolution. The top-level phrase 𝗎𝗌𝗂𝗇𝗀𝑃𝗂𝗇𝑇 checks that 𝑃∈R, that its interface is an admissible instance declaration, and that adding it preserves non-overlap. It then checks the top-level module phrase 𝑇 under Θ,𝑃. The phrase does not add 𝑃 globally, and 𝑃 need not be canonical in any sibling module.
The distinction gives three separate operations: operationeffectdeclare𝑃𝑃becomesanamedmoduleinRadopt𝑃𝑃becomesavailableintheselectedΘresolve𝐾[𝜏]constructevidencefromthelexicallyselectedΘ. Conflating the first two operations recreates the global-instance problem. Conflating the second and third hides configuration inside search.
The opening counterexample also shows why adoption cannot be inserted under an arbitrary expression and then forgotten at generalization. Let 𝑚∈𝖨𝗇𝗇𝖾𝗋(𝑆) mean that 𝑚 is an ordinary term or reduced-module phrase of chapter 12, checked at signature 𝑆, with no 𝗎𝗌𝗂𝗇𝗀 form. The outer boundary grammar is exactly 𝑇::=𝑚:>𝑆∣𝗎𝗌𝗂𝗇𝗀𝑃𝗂𝗇𝑇(𝑚∈𝖨𝗇𝗇𝖾𝗋(𝑆)). Only 𝑇 admits 𝗎𝗌𝗂𝗇𝗀. The ascription 𝑚:>𝑆 fixes the signature exported across the boundary. In the opening program, the signature of 𝐴 must therefore choose between 𝗏𝖺𝗅𝑓:𝖨𝗇𝗍→𝖡𝗈𝗈𝗅and𝗏𝖺𝗅𝑓:∀𝑋:𝖤𝖰.𝑋.𝑡→𝖡𝗈𝗈𝗅. The first captures 𝖤𝗊𝖨𝗇𝗍; the second advertises a module parameter and lets each caller supply it. Scope no longer guesses which interface the programmer intended.
The finite elaboration judgment begins after the exported signature has chosen one of these two types for 𝐴.𝑓. It inserts evidence for an already explicit class request; it does not decide whether 𝐴.𝑓 is generalized.
Let Θ be admissible, and let 𝐷 be a well-typed declaration satisfying the decreasing condition whose result head (𝐾,𝑐) does not occur in Θ. Then Θ,𝐷 is admissible. Every declaration already in Θ retains its interface.
Proof. Well-typedness and the decreasing condition hold for old declarations by the admissibility of Θ, and for 𝐷 by hypothesis. The only possible new overlap would involve 𝐷. Its result head is absent from Θ, so no such pair exists. Extending an availability environment does not alter the signatures of its existing paths or functors. ◻
★★☆ Let Θ contain 𝖤𝗊𝖨𝗇𝗍 and 𝖤𝗊𝖫𝗂𝗌𝗍. For each of the following proposed adoptions, say whether lemma 16.4 applies and give the exact reason: 𝖲𝗁𝗈𝗐𝖨𝗇𝗍:𝖲𝖧𝖮𝖶[𝖨𝗇𝗍],𝖤𝗊𝖭𝖾𝗏𝖾𝗋:𝖤𝖰[𝖨𝗇𝗍],𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍:𝖲𝖧𝖮𝖶[𝛼]⇒𝗆𝗈𝖽𝖲𝖧𝖮𝖶[𝖫𝗂𝗌𝗍(𝛼)]. For the rejected case, exhibit two distinct evidence terms for the same request if non-overlap were ignored.
Resolution should construct the omitted module expression and nothing else. The judgment Θ⊢𝐾[𝜏]⇓𝗋𝖾𝗌𝑉 means that the available declarations construct evidence 𝑉 for the realized signature 𝐾[𝜏]. It is generated by two rules.
These are the evidence judgments used in the resolver theorem; field typing is added only when the evidence is inserted into ordinary terms.
Resolve the pair request first and name its evidence 𝑉𝗉𝖺𝗂𝗋:=𝖤𝗊𝖯𝖺𝗂𝗋⟨𝖤𝗊𝖨𝗇𝗍,𝖤𝗊𝖡𝗈𝗈𝗅⟩. The pair evidence is derived by 𝖤𝗊𝖨𝗇𝗍∈ΘΘ⊢𝖤𝖰[𝖨𝗇𝗍]⇓𝗋𝖾𝗌𝖤𝗊𝖨𝗇𝗍R−Base𝖤𝗊𝖡𝗈𝗈𝗅∈ΘΘ⊢𝖤𝖰[𝖡𝗈𝗈𝗅]⇓𝗋𝖾𝗌𝖤𝗊𝖡𝗈𝗈𝗅R−BaseΘ⊢𝖤𝖰[𝖯𝖺𝗂𝗋(𝖨𝗇𝗍,𝖡𝗈𝗈𝗅)]⇓𝗋𝖾𝗌𝑉𝗉𝖺𝗂𝗋R−Functor. The outer application then has one premise: Θ⊢𝖤𝖰[𝖯𝖺𝗂𝗋(𝖨𝗇𝗍,𝖡𝗈𝗈𝗅)]⇓𝗋𝖾𝗌𝑉𝗉𝖺𝗂𝗋Θ⊢𝖤𝖰[𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝖨𝗇𝗍,𝖡𝗈𝗈𝗅))]⇓𝗋𝖾𝗌𝖤𝗊𝖫𝗂𝗌𝗍⟨𝑉𝗉𝖺𝗂𝗋⟩R−Functor.
A naive resolver might enumerate all module expressions and test their signatures. That search rediscovers irrelevant functors, repeats identical subproblems, and diverges in the presence of 𝖤𝗊𝖠𝗀𝖺𝗂𝗇. In 𝖬𝖳𝖢0, the result type already determines the only declaration that could finish a derivation. Resolution can therefore follow the pair (𝐾,𝑐) at the root.
For admissible Θ, the resolver is the diagnostic function 𝗋𝖾𝗌𝗈𝗅𝗏𝖾Θ(𝐾,𝜏) recursively on |𝜏|. Its result is either evidence 𝑉 or a diagnostic 𝗆𝗂𝗌𝗌𝗂𝗇𝗀(𝐾′,𝜏′); the induced evidence-only selector is partial. For 𝜏=𝑐(𝜏1,…,𝜏𝑛), look up the unique declaration with result head (𝐾,𝑐).
If no declaration exists, return 𝗆𝗂𝗌𝗌𝗂𝗇𝗀(𝐾,𝜏).
If it is a nullary path 𝑃, return 𝑃.
If it is a functor 𝐹 with premises 𝐾𝑟[𝛼𝑗𝑟], recursively resolve 𝐾𝑟[𝜏𝑗𝑟]. Return 𝐹⟨𝑉1,…,𝑉𝑚⟩ when all calls succeed, and propagate the first missing request otherwise.
An overlap is rejected when Θ is validated, before this function is called.
Proof of Theorem 16.6 — Exactness and uniqueness of MTC_0 resolution
Proof. For termination, recurse on |𝜏|. Every functor premise requests an immediate argument 𝜏𝑗𝑟, and |𝜏𝑗𝑟|<|𝑐(𝜏1,…,𝜏𝑛)|. A finite number of smaller calls therefore terminates.
For soundness, use the same induction. A returned nullary path gives R-Base. In the functor case, each recursive call gives Θ⊢𝐾𝑟[𝜏𝑗𝑟]⇓𝗋𝖾𝗌𝑉𝑟. Applying R-Functor gives Θ⊢𝐾[𝑐(¯𝜏)]⇓𝗋𝖾𝗌𝐹⟨𝑉1,…,𝑉𝑚⟩, and ordinary functor application typing gives Θ⊢𝐹⟨𝑉1,…,𝑉𝑚⟩:𝐾[𝑐(𝜏1,…,𝜏𝑛)].
For completeness, induct on the final rule of the assumed resolution derivation. In rule R-Base, the final premise selects a declaration with head (𝐾,𝑐); admissibility says that no second selected declaration has that head, so lookup returns this declaration uniquely. In rule R-Functor, the same non-overlap condition proves that 𝐹 is the only possible root declaration. Each premise has the unique evidence returned by the induction hypothesis, so every derivation of the request has the same outer constructor and subtrees.
For uniqueness, apply completeness to both derivations. A deterministic function cannot return two different results for one input. ◻
Proof of Corollary 16.7 — Resolution is stable under admissible extension
Proof. Induct on the evidence tree 𝑉. At each node the declaration used in Θ remains present in Θ′. Admissibility of Θ′ prevents any second declaration at that result head. The recursive premise lookups are unchanged by the induction hypotheses. ◻
★☆☆ Let Θ contain 𝖤𝗊𝖨𝗇𝗍, 𝖤𝗊𝖯𝖺𝗂𝗋, and 𝖤𝗊𝖫𝗂𝗌𝗍, but not 𝖤𝗊𝖡𝗈𝗈𝗅. Trace 𝗋𝖾𝗌𝗈𝗅𝗏𝖾Θ on 𝖤𝖰[𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝖨𝗇𝗍,𝖡𝗈𝗈𝗅))]. Give the sequence of lookup keys in depth-first left-to-right order, the first reported missing request, and the derivation rule whose premise cannot be completed.
To elaborate 𝖾𝗊[𝜏](𝑒1,𝑒2), resolve 𝖤𝖰[𝜏]⇓𝗋𝖾𝗌𝑉, elaborate both arguments at 𝜏, and return 𝑉.𝖾𝗊𝑒′1𝑒′2. Consider the ground source fragment with explicit type indices: 𝑒::=𝑥∣𝜆𝑥:𝜏.𝑒∣𝑒𝑒∣𝖾𝗊[𝜏](𝑒,𝑒)∣𝗌𝗁𝗈𝗐[𝜏](𝑒).
The target’s ordinary fragment is the simply typed lambda calculus on these ground types. Its module-evidence expressions are 𝑉::=𝑃∣𝐹⟨𝑉1,…,𝑉𝑚⟩,𝑒′::=𝑥∣𝑐∣𝜆𝑥:𝜏.𝑒′∣𝑒′𝑒′∣𝑉.𝑓, where 𝑐 ranges over the ground literals and primitives. The environment Θ binds every path and total functor at its realized interface. Write ¯𝐾[¯𝛼] for 𝐾1[𝛼𝑗1],…,𝐾𝑚[𝛼𝑗𝑚]. Besides the ordinary lambda rules, target typing has 𝑃:𝐾[𝑐]∈ΘΓ;Θ⊢𝑃:𝐾[𝑐]Mod−PathΘ(𝐹)=¯𝐾[¯𝛼]⇒𝗆𝗈𝖽𝐾[𝑐(¯𝛼)]Γ;Θ⊢𝑉𝑟:𝐾𝑟[𝜏𝑗𝑟](1≤𝑟≤𝑚)Γ;Θ⊢𝐹⟨𝑉1,…,𝑉𝑚⟩:𝐾[𝑐(¯𝜏)]Mod−Functor. and Γ;Θ⊢𝑉:𝐾[𝜏]𝑓:𝐴(𝑡)isafieldof𝐾Γ;Θ⊢𝑉.𝑓:𝐴(𝜏)Mod−Field, Thus 𝐹⟨¯𝑉⟩ has its declared realized result when every 𝑉𝑖 has the corresponding realized premise interface. These rules type the finite slice’s module evidence.
Proof of Lemma 16.8 — Evidence embedding into the term context
Proof. Induct on the evidence-typing derivation. An application of T-EvPath becomes Mod-Path. In the T-EvFunctor case, apply the induction hypotheses to every evidence argument and finish with Mod-Functor. These are the two evidence forms. ◻
Ordinary forms elaborate homomorphically. The two interesting rules are
Θ⊢𝖤𝖰[𝜏]⇓𝗋𝖾𝗌𝑉Γ;Θ⊢𝑒𝑖:𝜏⇝𝑒′𝑖(𝑖=1,2)
Γ;Θ⊢𝖾𝗊[𝜏](𝑒1,𝑒2):𝖡𝗈𝗈𝗅⇝𝑉.𝖾𝗊𝑒′1𝑒′2
E-Eq
Θ⊢𝖲𝖧𝖮𝖶[𝜏]⇓𝗋𝖾𝗌𝑉Γ;Θ⊢𝑒:𝜏⇝𝑒′
Γ;Θ⊢𝗌𝗁𝗈𝗐[𝜏](𝑒):𝖲𝗍𝗋𝗂𝗇𝗀⇝𝑉.𝗌𝗁𝗈𝗐𝑒′
E-Show
For example, let 𝜏0:=𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝖨𝗇𝗍,𝖡𝗈𝗈𝗅)),𝑉0:=𝖤𝗊𝖫𝗂𝗌𝗍⟨𝖤𝗊𝖯𝖺𝗂𝗋⟨𝖤𝗊𝖨𝗇𝗍,𝖤𝗊𝖡𝗈𝗈𝗅⟩⟩. The premise Θ⊢𝖤𝖰[𝜏0]⇓𝗋𝖾𝗌𝑉0 and two variable rules give 𝑥:𝜏0,𝑦:𝜏0;Θ⊢𝖾𝗊[𝜏0](𝑥,𝑦):𝖡𝗈𝗈𝗅⇝𝑉0.𝖾𝗊𝑥𝑦. The target records the module expression selected by resolution.
Assume Θ is admissible and every module field projection obeys its realized signature. If Γ;Θ⊢𝑒:𝜏⇝𝑒′, then Γ;Θ⊢𝑒′:𝜏 in the explicit module target, where the declarations in Θ are bound at their displayed module interfaces.
Proof of Proposition 16.9 — Type preservation of evidence insertion
Proof. Induct on the elaboration derivation. Variables, abstractions, and applications are the corresponding target typing rules. In E-Eq, theorem 16.6 gives Θ⊢𝑉:𝖤𝖰[𝜏]. By lemma 16.8, the same evidence is typed under Γ;Θ, and signature projection gives Γ;Θ⊢𝑉.𝖾𝗊:𝜏→𝜏→𝖡𝗈𝗈𝗅. The induction hypotheses type 𝑒′1 and 𝑒′2 at 𝜏; two applications give the conclusion. The E-Show case is identical with field type 𝜏→𝖲𝗍𝗋𝗂𝗇𝗀. ◻
The proposition is preservation, not a theorem that every source program has an elaboration. Missing evidence, rejected overlap, and an unannotated ambiguous type can all prevent a derivation.
★★☆ Assume the 𝖲𝖧𝖮𝖶 base modules and functors displayed in section 16.1. Elaborate 𝜆𝑧:𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝖡𝗈𝗈𝗅,𝖨𝗇𝗍)).𝗌𝗁𝗈𝗐[𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝖡𝗈𝗈𝗅,𝖨𝗇𝗍))](𝑧). Then give a target typing derivation for the inserted field projection and state exactly where admissibility is used.
Ground resolution constructs a closed module expression. Type inference must also handle requests containing unknown types. Consider 𝜆𝑥:𝛼.𝖾𝗊[𝖯𝖺𝗂𝗋(𝖨𝗇𝗍,𝛼)]((0,𝑥),(0,𝑥)). The outer constructor already determines 𝖤𝗊𝖯𝖺𝗂𝗋. The integer premise is solved by 𝖤𝗊𝖨𝗇𝗍, while the request 𝖤𝖰[𝛼] remains. Introduce an evidence variable 𝑋:𝖤𝖰[𝛼]. The partially constructed evidence is 𝖤𝗊𝖯𝖺𝗂𝗋⟨𝖤𝗊𝖨𝗇𝗍,𝑋⟩, and the generalized target is Λ𝛼.𝜆𝑋:𝖤𝖰[𝛼].𝜆𝑥:𝛼.(𝖤𝗊𝖯𝖺𝗂𝗋⟨𝖤𝗊𝖨𝗇𝗍,𝑋⟩).𝖾𝗊((0,𝑥),(0,𝑥)). The residual constraint is not a failure. It is the interface exported by generalization.
A symbolic request is 𝐾[𝜌], where 𝜌 may contain type variables. Normalize type variables to de Bruijn indices and fix a total order on requests: first the fixed class-name order, then the lexicographic order on normalized type syntax. Reduction maintains a memo table from requests to evidence expressions.
If 𝐾[𝜌] is already in the table, reuse its recorded evidence.
If 𝜌 is a bare type variable, record one residual hole for the request and return that hole.
If 𝜌=𝑐(¯𝜌), follow the unique declaration at head (𝐾,𝑐), reduce its premises from left to right, and record the resulting path or functor application. A missing head is reported as in ground resolution.
After the root call, retain the distinct residual requests, sort them by the fixed order, name them 𝑋1,…,𝑋𝑞, and replace every memoized hole by the corresponding variable. Thus repeated requests share one parameter, and the exported parameter order is independent of traversal accidents. The output is a pair (Σ,𝑉), where Σ=𝑋1:𝐾1[𝛼1],…,𝑋𝑞:𝐾𝑞[𝛼𝑞] is the residual evidence context and 𝑉 is an evidence expression well typed under Θ,Σ.
For example, reducing 𝖤𝖰[𝖯𝖺𝗂𝗋(𝛼,𝛼)] produces (𝑋:𝖤𝖰[𝛼],𝖤𝗊𝖯𝖺𝗂𝗋⟨𝑋,𝑋⟩), not two independently generalized parameters.
Here Σ denotes the residual module-evidence context; in lemma 16.11, lowercase 𝜎 denotes an evidence substitution.
For the request 𝖤𝖰[𝖯𝖺𝗂𝗋(𝖨𝗇𝗍,𝛼)], reduction returns (𝑋:𝖤𝖰[𝛼],𝖤𝗊𝖯𝖺𝗂𝗋⟨𝖤𝗊𝖨𝗇𝗍,𝑋⟩). Substituting any ground evidence 𝑊:𝖤𝖰[𝜏] for 𝑋 yields well-typed evidence at 𝖯𝖺𝗂𝗋(𝖨𝗇𝗍,𝜏). It yields exactly the evidence obtained by ground resolution when 𝑊 is itself the evidence selected by the ground resolver for 𝖤𝖰[𝜏].
Let Θ be admissible, and suppose constraint reduction returns (Σ,𝑉) for 𝐾[𝜌]. Let 𝜂 replace every type variable in 𝜌 by a ground type. Let 𝜎 map each assumption 𝑋:𝐾𝑋[𝛼𝑋] in Σ to evidence satisfying Θ⊢𝜎𝑋:𝐾𝑋[𝜂𝛼𝑋]. Then Θ⊢𝜎𝑉:𝐾[𝜂𝜌]. If, in addition, 𝗋𝖾𝗌𝗈𝗅𝗏𝖾Θ(𝐾𝑋,𝜂𝛼𝑋)=𝜎𝑋forevery𝑋:𝐾𝑋[𝛼𝑋]∈Σ, then 𝗋𝖾𝗌𝗈𝗅𝗏𝖾Θ(𝐾,𝜂𝜌)=𝜎𝑉.
Proof of Lemma 16.11 — Grounding residual evidence
Proof. Induct on the number of memo-table insertions, with a subsidiary induction on the constructor depth of a newly inserted request. A memo hit performs no insertion: it reuses evidence already typed by the induction hypothesis, and 𝜎 acts on that shared expression only once. A retained variable request is typed after substitution by the first hypothesis on 𝜎. Under the additional hypothesis, it is also resolved to 𝜎𝑋. A solved nullary request is typed and resolved by its declaration. For a functor node, apply the induction hypotheses to the premise evidence and then the functor’s declared interface. After grounding, the resolver follows the same unique head declaration and, by the induction hypotheses, returns the substituted premise subtrees. It therefore returns 𝜎𝑉. In the final renaming pass, every occurrence of one memoized residual hole is replaced by the same 𝑋𝑖. Renaming the finite residual telescope preserves typing, so sorting and coalescing cannot duplicate an assumption or change the evidence type. ◻
★☆☆ Reduce 𝖲𝖧𝖮𝖶[𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝛼,𝖫𝗂𝗌𝗍(𝖨𝗇𝗍)))] under 𝖲𝗁𝗈𝗐𝖨𝗇𝗍, 𝖲𝗁𝗈𝗐𝖯𝖺𝗂𝗋, and 𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍. Give the residual context, the evidence expression, and its result after grounding 𝛼 to 𝖡𝗈𝗈𝗅 with evidence 𝖲𝗁𝗈𝗐𝖡𝗈𝗈𝗅.
The source is the extended technical presentation dated 26 October 2006 and the POPL 2007 publication. Its external language elaborates into an explicitly typed higher-order module language. It adds the following load-bearing forms to ordinary terms, signatures, modules, and top-level modules: sig::=⋯∣𝖼𝖺𝗇𝗈𝗇(sig),mod::=⋯∣𝗈𝗏𝖾𝗋𝗅𝗈𝖺𝖽ℓ𝖿𝗋𝗈𝗆sig∣𝗂𝗆𝗉𝗅𝗂𝖼𝗂𝗍(𝑃)∣𝖾𝗑𝗉𝗅𝗂𝖼𝗂𝗍(𝑃:𝑆),top::=⋯∣𝑚:>𝑆∣𝗎𝗌𝗂𝗇𝗀𝑃𝗂𝗇top. The internal language distinguishes ordinary functors from total functors; canonical instance construction may apply only the latter. The set Θ contains paths explicitly adopted as canonical. Declarative elaboration uses judgments for terms, modules, top-level modules, coercive signature matching, class decomposition, usability, and canonical module construction. Type inference extends Algorithm W with substitutions and residual module constraints.
First, 𝖼𝖺𝗇𝗈𝗇(𝑆) requests the canonical module at a concrete class signature 𝑆. Its class parameters must be transparent, but associated type components may remain abstract. Thus a class can expose the selected carrier while retaining additional type-level information supplied by the instance.
Second, 𝗈𝗏𝖾𝗋𝗅𝗈𝖺𝖽𝖾𝗊𝖿𝗋𝗈𝗆𝖤𝖰 elaborates to a constrained polymorphic value whose module parameter supplies the projection 𝑋.𝖾𝗊. Instantiating that value finds a canonical module of a transparent realization of 𝖤𝖰 and inserts it as a total-functor argument.
Third, 𝗎𝗌𝗂𝗇𝗀𝑃𝗂𝗇𝑇 first checks that 𝑃 is usable, including the source calculus’s structural non-overlap test, and only then checks 𝑇 under Θ,𝑃. The form exists only at the top-module stratum, which is the formal repair for the scope/generalization failure at the beginning of the chapter.
Canonical evidence is not restricted to a named base path. The selected rules admit:
singleton modules for transparent type components;
componentwise canonical structures;
a path 𝑃∈Θ at its atomic class signature;
application of a canonical total functor from Θ to canonical argument evidence; and
conversion along equivalent signatures.
The fourth clause is the full-calculus analogue of R-Functor.
The inference algorithm does not immediately guess closed evidence for every constraint. It produces a type-and-module substitution 𝛿 and a constraint context Σ. Constraint normalization performs canonical backchaining. For example, an equality constraint at 𝖯𝖺𝗂𝗋(𝖨𝗇𝗍,𝛼) reduces to a residual equality constraint at 𝛼, with evidence 𝖤𝗊𝖯𝖺𝗂𝗋⟨𝖤𝗊𝖨𝗇𝗍,𝑋⟩. This is the higher-order, structural-signature version of definition 16.10.
An arrow ⇒ records an algorithmic output; its decorated form ⇒↓ additionally says that residual constraints have been normalized. A suffix /(Σ;𝛿) contains the residual module constraints and inferred substitution. The relations ⪯, ⇓𝖼𝗇, and ⇝𝖼𝗇 are, respectively, coercive signature matching and the two constraint-processing phases. The arrow ⇝ is the source’s declarative elaboration separator. Thus the declarative expression and module judgments have signatures Θ;Γ⊢exp⇝𝑒:𝜏,Θ;Γ⊢mod⇝𝑀:𝑆. Here exp and mod are source expressions and modules, whereas 𝑒 and 𝑀 are their intermediate-language elaborations.
The soundness hypotheses use the following definitions from Figure 22. Write ⟨⟨𝐾⟩⟩𝗌 for the atomic signature containing one kind component 𝐾, and ⟨⟨𝜏⟩⟩𝗌 for the atomic signature containing one type component at 𝜏; this signature embedding is not semantic denotation. An intermediate-language object is ground when it has no free unification variables. A kind is legal when it is the image of an external-language kind. A signature is legal when every embedded signature ⟨⟨𝐾⟩⟩𝗌 contains a legal kind, every result of a dependent functor Π𝑋:𝑆1.𝑆2 is a structure signature, and every result of ∀𝑋:𝑆1.𝑆2 is either a structure signature or ⟨⟨𝜏⟩⟩𝗌; in the latter case Γ⊢𝑆1𝖼𝗅𝖺𝗌𝗌 must hold in the context where 𝑆1 occurs. A context is legal when each kind and signature declared in it is legal. The imported judgments maintain legality as an invariant.
A signature 𝑆 is synthesis when every signature occurring within it is ground, except that a sub-signature ⟨⟨𝜏⟩⟩𝗌 or ∀𝑋:𝑅.⟨⟨𝜏⟩⟩𝗌 may be non-ground when it does not occur in the argument of a functor signature. A context Γ is synthesis exactly when every declaration 𝛼:𝐾 has ground 𝐾 and every declaration 𝑋:𝑆 has synthesis 𝑆. The context (Θ;Γ) is valid for inference exactly when ⊢(Θ;Γ)𝗈𝗄,Γissynthesis,𝑃∈Θ⟹sigΓ(𝑃)isground. Here sigΓ(𝑃) is the signature assigned to 𝑃 by Γ. The premise ⊢(Θ;Γ)𝗈𝗄 abbreviates the source’s two context-formation relations: 𝑋⊢∅𝗈𝗄⊢Γ𝗈𝗄Γ⊢𝐾𝗄𝗂𝗇𝖽⊢Γ,𝛼:𝐾𝗈𝗄,⊢Γ𝗈𝗄Γ⊢𝜏:𝖳⊢Γ,𝑥:𝜏𝗈𝗄⊢Γ𝗈𝗄Γ⊢𝑆𝗌𝗂𝗀⊢Γ,𝑋:𝑆𝗈𝗄. and ⊢Γ𝗈𝗄⊢(∅;Γ)𝗈𝗄⊢(Θ;Γ)𝗈𝗄Θ;Γ⊢𝑃𝗎𝗌𝖺𝖻𝗅𝖾⊢(Θ,𝑃;Γ)𝗈𝗄.
Let Γ0 and Γ1 be contexts, and let 𝜃 be a type substitution. Define the context-substitution judgment by Γ0⊢𝜃:Γ1⟺⊢Γ0𝗈𝗄andΓ0⊇𝜃Γ1,∀𝛼∈dom(𝜃).Γ0⊢𝜃𝛼:𝖳. For a constraint context Σ, define simultaneous canonical evidence by Θ;Γ⊢𝖼𝖺𝗇𝜎:Σ⟺∀(𝑋:𝑆)∈Σ.Θ;Γ⊢𝖼𝖺𝗇𝜎𝑋:𝑆.
The selected interface has the following judgment signatures: Θ;Γ⊢exp⇒𝑒:𝜏/(Σ;𝛿),Θ;Γ⊢exp⇒↓𝑒:𝜏/(Σ;𝛿),Θ;Γ⊢mod⇒𝑀:𝑆/(Σ;𝛿),Θ;Γ⊢mod⇒↓𝑀:𝑆/(Σ;𝛿),Θ;Γ⊢Σ0⇓𝖼𝗇(Σ;𝜎;𝛿),Θ;Γ⊢Σ0⇝𝖼𝗇(Σ;𝜎;𝛿),Θ;Γ⊢𝖼𝖺𝗇𝜎:Σ,Γ0⊢𝜃:Γ1,Θ;Γ⊢top⇒𝑀:𝑆,Θ;Γ⊢top⇝𝑀:𝑆. These are the expression, module, constraint-processing, substitution, canonical-evidence, and top-level fragments of Figures 22–23 in the extended presentation [DHCK07]. No rule outside that named source system is implicit in the notation.
Let the algorithmic judgments be those of convention 16.13. If (Θ;Γ) is valid for inference, then each selected expression, module, or constraint-processing judgment with input Θ;Γ and output substitution 𝛿 satisfies 𝛿Γ⊢𝛿:Γ. Suppose further that Θ′⊇Θ, Γ′⊢𝛿′:𝛿Γ, and ⊢(Θ′;Γ′)𝗈𝗄. Suppose also that Θ′;Γ′⊢𝖼𝖺𝗇𝜎′:𝛿′Σ. Then the four projections used in this chapter hold:
If either Θ;Γ⊢exp⇒𝑒:𝜏/(Σ;𝛿) or Θ;Γ⊢exp⇒↓𝑒:𝜏/(Σ;𝛿), then Θ′;Γ′⊢exp⇝𝜎′𝛿′𝑒:𝛿′𝜏.
If either Θ;Γ⊢mod⇒𝑀:𝑆/(Σ;𝛿) or Θ;Γ⊢mod⇒↓𝑀:𝑆/(Σ;𝛿), then Θ′;Γ′⊢mod⇝𝜎′𝛿′𝑀:𝛿′𝑆.
If constraint normalization returns Θ;Γ⊢Σ0⇓𝖼𝗇(Σ;𝜎;𝛿) or Θ;Γ⊢Σ0⇝𝖼𝗇(Σ;𝜎;𝛿), then Θ′;Γ′⊢𝖼𝖺𝗇𝜎′𝛿′𝜎:𝛿′𝛿Σ0.
If ⊢(Θ;Γ)𝗈𝗄, Γ is ground, and Θ;Γ⊢top⇒𝑀:𝑆, then Θ;Γ⊢top⇝𝑀:𝑆, and 𝑆 is ground.
Proof of Theorem 16.14 — Imported: published inference soundness
Imported proof.Source. The soundness theorem in [DHCK07] supplies clauses 5, 7, 9, and the final top-level clause of its mutual statement: these are respectively the expression, module, constraint-processing, and top-level projections used here. The preceding convention fixes the signature used here. Solving residual constraints by 𝜎′ gives the displayed judgments in the compatible extension Θ′;Γ′. No completeness, coherence, or result for 𝖬𝖳𝖢0 is imported. ◻
The expression 𝗌𝗁𝗈𝗐(𝗋𝖾𝖺𝖽("𝟷")) can leave the intermediate carrier unconstrained. Distinct reader/printer module expressions can then solve the constraints, so the algorithm rejects the ambiguity. Soundness types one returned elaboration; coherence would compare all returned elaborations.
Modular implicits: evidence is a module expression
The examples in this section follow OCaml surface notation: list is postfix and a module functor is applied with parentheses. The formal 𝖬𝖳𝖢0 calculus above retains prefix 𝖫𝗂𝗌𝗍(𝐴) and angle brackets 𝐹⟨𝑉⟩; the notation change does not identify the two resolution systems.
Modular implicits move the implicit parameter into ordinary function syntax. For a module type 𝑆, a function may have an implicit module parameter {𝑀:𝑆}→𝜏. An explicit call writes 𝑓{𝑀}𝑥; an omitted argument asks the resolver to construct a module expression. Candidate modules enter the search space through 𝗂𝗆𝗉𝗅𝗂𝖼𝗂𝗍𝗆𝗈𝖽𝗎𝗅𝖾, local implicit-module bindings, implicit parameters, and explicitly opened implicit namespaces.
With 𝗌𝗁𝗈𝗐{𝑆:𝖲𝖧𝖮𝖶}(𝑥:𝑆.𝑡)=𝑆.𝗌𝗁𝗈𝗐𝑥,𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍{𝑆:𝖲𝖧𝖮𝖶}:𝖲𝖧𝖮𝖶with𝑡=𝑆.𝑡𝗅𝗂𝗌𝗍, the call 𝗌𝗁𝗈𝗐[1,2] constrains the missing module 𝑀 by 𝑀.𝑡=𝖨𝗇𝗍𝗅𝗂𝗌𝗍. Trying the functor 𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍 transforms that constraint into 𝑆.𝑡=𝖨𝗇𝗍, which 𝖲𝗁𝗈𝗐𝖨𝗇𝗍 solves. The evidence is the module expression 𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍(𝖲𝗁𝗈𝗐𝖨𝗇𝗍), not merely the name of a global dictionary.
Let I be the finite, lexically ordered collection of implicit module paths and total implicit-module functors currently in scope. Write I;Γ⊢𝑆⇓𝖼𝖺𝗇𝖽{𝑉1,…,𝑉𝑛} for candidate search after inference has generated the type-component equations for an omitted parameter of module type 𝑆. A module path is a candidate when its signature matches 𝑆. A functor application 𝐹(𝑉1,…,𝑉𝑛) is a candidate when its result matches 𝑆 and each strictly earlier argument request has the unique candidate 𝑉𝑖. Reject cycles and nondecreasing self-applications, and quotient the resulting finite set by alpha-equivalence. The omitted-call boundary is I;Γ⊢𝑆⇓𝖼𝖺𝗇𝖽{𝑉}Γ⊢𝑓:{𝑀:𝑆}→𝜏1→𝜏2Γ⊢𝑥:𝜏1I;Γ⊢𝑓𝑥⇝𝑓{𝑉}𝑥:𝜏2MI−Call. Zero candidates is a missing-implicit error; two distinct candidates is an ambiguity error. For the list example the carrier equation first admits 𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍(𝑆), reduces its premise to 𝑆.𝑡=𝖨𝗇𝗍, and closes uniquely with 𝑆=𝖲𝗁𝗈𝗐𝖨𝗇𝗍. If both 𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍 and 𝖢𝗈𝗆𝗉𝖺𝖼𝗍𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍 are eligible, the same calculation produces two candidates and MI-Call is inapplicable.
The published design resolves one omitted implicit module in three stages.
Type inference gathers equations constraining the missing module’s type components from explicit arguments and the expected result.
Search constructs module expressions from unqualified implicit modules and implicit functors in scope, using module-type inclusion and the gathered equations to test candidates.
The call is accepted only when the resulting module expression is unique up to the implementation’s alias-equivalence test. Search must also satisfy the specified decreasing check for repeated applications of the same implicit functor.
The decreasing check compares the constraints passed to successive applications of one functor. Thus 𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍(𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍(𝖲𝗁𝗈𝗐𝖨𝗇𝗍)) is allowed when resolving an integer-list-list request: the carrier constraint loses one list constructor at each repeated application. In contrast, 𝖲𝗁𝗈𝗐𝖠𝗀𝖺𝗂𝗇(𝖲𝗁𝗈𝗐𝖠𝗀𝖺𝗂𝗇(⋯)) with 𝖲𝗁𝗈𝗐𝖠𝗀𝖺𝗂𝗇(𝑆).𝑡=𝑆.𝑡 does not decrease and is rejected. Termination is required before uniqueness can be checked; silently ignoring a nonterminating branch could miss a second candidate.
Uniqueness concerns complete module expressions. If the two candidates are 𝑉1=𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍(𝖲𝗁𝗈𝗐𝖨𝗇𝗍),𝑉2=𝖢𝗈𝗆𝗉𝖺𝖼𝗍𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍(𝖲𝗁𝗈𝗐𝖨𝗇𝗍), then the call is ambiguous even if both modules print some legal string. If two inheritance paths construct extensionally similar modules but are not recognized aliases, they also remain distinct candidates. Resolution is therefore not ordered by a hidden preference rule.
Resolving 𝖲𝖧𝖮𝖶 for a list of pairs constructs the nested evidence 𝑉𝗉𝖺𝗂𝗋=𝖲𝗁𝗈𝗐𝖯𝖺𝗂𝗋(𝖲𝗁𝗈𝗐𝖲𝗍𝗋𝗂𝗇𝗀,𝖲𝗁𝗈𝗐𝖨𝗇𝗍),𝑉𝗅𝗂𝗌𝗍=𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍(𝑉𝗉𝖺𝗂𝗋).
Prototype specimen.
The archived modular-implicits checkout recorded in appendix E contains the regression example testsuite/tests/typing-modular_implicits/show.ml, whose observations are
"4"
5
[("hello",1); ("world",2)]
5.5
Appendix E records the checkout identity separately from the Kappa companion.
The archived prototype output confirms that this module expression elaborates the sample list [WBY15].
★☆☆ Assume the following implicit modules are available: 𝖲𝗁𝗈𝗐𝖨𝗇𝗍,𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍,𝖢𝗈𝗆𝗉𝖺𝖼𝗍𝖲𝗁𝗈𝗐𝖫𝗂𝗌𝗍. The two functors have the same result carrier 𝑆.𝑡𝗅𝗂𝗌𝗍 but different printing operations. Resolve 𝗌𝗁𝗈𝗐[1,2] as far as possible. List every candidate module expression, state the common generated constraint, and explain why the decreasing check succeeds while uniqueness fails.
Implicit function types are lexically scoped functions
An implicit function need not denote a canonical class instance or a module. Consider the source term 𝗅𝖾𝗍?:𝖨𝗇𝗍=1𝗂𝗇𝗅𝖾𝗍𝑓:𝖨𝗇𝗍?→𝖨𝗇𝗍=?𝗂𝗇𝗅𝖾𝗍?:𝖨𝗇𝗍=2𝗂𝗇𝑓. The body used to define 𝑓 is checked under a fresh implicit integer parameter, so it elaborates to the identity function. At the final occurrence of 𝑓, automatic implicit application chooses the innermost integer, namely 2. The result is 2. There is no globally canonical 𝖨𝗇𝗍 evidence, no distinguished type component, and no module expression to synthesize.
SI has ordinary arrows, implicit arrows, polymorphism, explicit variables, and query ?; both arrows elaborate to ordinary System F arrows [OBL^+18]. Restricted types and full types are 𝑅::=𝑏∣𝑋∣𝑇→𝑇,𝑇::=𝑅∣𝑇?→𝑇∣∀𝑋.𝑇,𝑏::=𝖨𝗇𝗍. Terms include explicit variables and functions, the query ?, ordinary and implicit let bindings, type abstraction/application, and a stitching annotation. Bidirectional typing simultaneously elaborates into System F. Integer literals and addition elaborate homomorphically and never invoke implicit search.
The source uses one ordered context Γ, writing explicit bindings as 𝑥:𝑇 and implicit bindings as ?𝑦:𝑇. The tags are part of the context syntax: 𝑥 ranges over explicit variables and 𝑦 over implicit variables, so the two membership tests below are disjoint. The judgments Γ⊢𝑒⇒𝑇⇝𝑢 and Γ⊢𝑒⇐𝑇⇝𝑢 respectively synthesize and check while producing a System F term. The type translation (−)∗ maps both ordinary and implicit arrows to ordinary arrows and is homomorphic elsewhere. The typing and elaboration rules are:
𝑥:𝑇∈Γ
Γ⊢𝑥⇒𝑇⇝𝑥
SI-Var
?𝑦:𝑇∈Γ
Γ⊢?⇒𝑇⇝𝑦
SI-Query
Γ,𝑥:𝑆⊢𝑒⇐𝑇⇝𝑢
Γ⊢𝜆𝑥.𝑒⇐𝑆→𝑇⇝𝜆𝑥:𝑆∗.𝑢
SI-ArrI
Γ⊢𝑒1⇒𝑆→𝑇⇝𝑢Γ⊢𝑒2⇐𝑆⇝𝑢′
Γ⊢𝑒1𝑒2⇒𝑇⇝𝑢𝑢′
SI-ArrE
𝑦𝖿𝗋𝖾𝗌𝗁Γ,?𝑦:𝑆⊢𝑒⇐𝑇⇝𝑢
Γ⊢𝑒⇐𝑆?→𝑇⇝𝜆𝑦:𝑆∗.𝑢
SI-ImpI
Γ⊢𝑒⇒𝑆?→𝑇⇝𝑢Γ⊢?⇐𝑆⇝𝑢′
Γ⊢𝑒⇒𝑇⇝𝑢𝑢′
SI-ImpE
Γ,𝑋⊢𝑒⇐𝑇⇝𝑢
Γ⊢𝑒⇐∀𝑋.𝑇⇝Λ𝑋.𝑢
SI-AllI
Γ⊢𝑒⇒∀𝑋.𝑇⇝𝑢𝖿𝗍𝗏(𝑆)⊆dom𝗍𝗒(Γ)
Γ⊢𝑒⇒[𝑋:=𝑆]𝑇⇝𝑢[𝑆∗]
SI-AllE
Γ⊢𝑒1⇐𝑇⇝𝑢Γ,𝑥:𝑇⊢𝑒2⇒𝑅⇝𝑢′
Γ⊢𝗅𝖾𝗍𝑥:𝑇=𝑒1𝗂𝗇𝑒2⇒𝑅⇝(𝜆𝑥:𝑇∗.𝑢′)𝑢
SI-LetEx
Γ⊢𝑒1⇐𝑇⇝𝑢𝑦𝖿𝗋𝖾𝗌𝗁Γ,?𝑦:𝑇⊢𝑒2⇒𝑅⇝𝑢′
Γ⊢𝗅𝖾𝗍?:𝑇=𝑒1𝗂𝗇𝑒2⇒𝑅⇝(𝜆𝑦:𝑇∗.𝑢′)𝑢
SI-LetIm
Γ⊢𝑒⇒𝑅⇝𝑢
Γ⊢𝑒⇐𝑅⇝𝑢
SI-Stitch
The checking query in SI-ImpE is therefore a SI-Query synthesis followed by SI-Stitch when its type is restricted. Crucially, SI-Query itself permits any matching implicit binding. Lexical priority is imposed on complete derivations by definition 16.16. Declaratively, SI-AllE may instantiate ∀𝑋.𝑇 with any well-formed 𝑆. Algorithmically, synthesis creates a fresh metavariable 𝛼, elaborates the consumer, and solves 𝛼 when an application or checking judgment next constrains its expected type.
For the opening SI term, write the three implicit variables as 𝑖1,𝑖𝑓,𝑖2. The definition of 𝑓 checks as ?𝑖1:𝖨𝗇𝗍,?𝑖𝑓:𝖨𝗇𝗍⊢?⇒𝖨𝗇𝗍⇝𝑖𝑓, so implicit introduction gives 𝑓⇝𝜆𝑖𝑓:𝖨𝗇𝗍.𝑖𝑓. At the final use, implicit elimination generates a query at 𝖨𝗇𝗍; the rightmost eligible binding is 𝑖2. The complete target therefore contains (𝜆𝑖𝑓:𝖨𝗇𝗍.𝑖𝑓)𝑖2⟶∗𝑖2, and the enclosing explicit lets substitute 2 for 𝑖2.
A typing derivation is well scoped when every query selects the rightmost eligible implicit entry. If a query subderivation 𝐷 ends in SI-Query, write res(𝐷) for that variable. Formally, for every query subderivation 𝐷′ and every other derivation 𝐷″ of the same query judgment, either res(𝐷′)=res(𝐷″), or res(𝐷″) is defined to the left of res(𝐷′) in Γ. This condition resolves shadowing; it does not guarantee termination when eligible values themselves require implicit arguments.
The derivation that selects the earlier binding 𝑖1 in the opening shadowing context is not well scoped. If 𝐷1 selects 𝑖1 and 𝐷2 selects the later eligible 𝑖2, then res(𝐷1)=𝑖1,res(𝐷2)=𝑖2,𝑖1liestotheleftof𝑖2. The defining comparison therefore fails for 𝐷1 and succeeds for 𝐷2.
the translation of a closed well-typed SI term into System F preserves its translated type;
in the monomorphic fragment without polymorphic function types, a term has at most one well-scoped synthesis derivation at a restricted type, and checking a term against a given type has at most one well-scoped derivation;
if the published 𝗌𝗒𝗇𝗍𝗁 procedure returns a type and target, the corresponding synthesis derivation exists; if 𝖼𝗁𝖾𝖼𝗄 returns a target at a supplied type, the corresponding checking derivation exists; and
semi-completeness for terms with no query in synthesis position is stated as a conjecture, with divergence permitted.
Imported proof.Source. Items 1–3 are Theorem 3.1 and Propositions 3.6–3.8 of [OBL^+18], specialized to the SI calculus fixed above. For example, SI-ImpE combines a function of type 𝑆∗→𝑇∗ with the checked query of type 𝑆∗. Item 4 records Conjecture 3.9, not a proved result. The monomorphic restriction and possible divergence are retained. ◻
This calculus answers the section’s question. An implicit function arrow elaborates to an ordinary function arrow, so it is dictionary-like in the weak sense that omitted values become explicit parameters. It differs in what those values mean and how they are found. SI searches lexical values by type and shadowing order. 𝖬𝖳𝖢0 and modular type classes construct class modules indexed by a distinguished carrier. Modular implicits search ordinary module expressions constrained by module types and type equations. The target may look like argument passing in all three cases, but the source static disciplines are not interchangeable.
★★☆ Elaborate and reduce 𝗅𝖾𝗍?:𝖨𝗇𝗍=10𝗂𝗇𝗅𝖾𝗍𝑎𝑑𝑑:𝖨𝗇𝗍?→(𝖨𝗇𝗍→𝖨𝗇𝗍)=𝜆𝑥.𝑥+?𝗂𝗇𝗅𝖾𝗍?:𝖨𝗇𝗍=3𝗂𝗇𝑎𝑑𝑑4. Indicate separately the query resolved while checking the body of 𝑎𝑑𝑑 and the query generated by automatic implicit elimination at its use. Explain why only one of them observes the inner binding 3.
The chapter has used three different evidence disciplines.
Mechanism
Implicit object
Search key
Scope rule
𝖬𝖳𝖢0 and modular classes
class module
class name and carrier head
top-level 𝗎𝗌𝗂𝗇𝗀 declarations
Modular implicits
module expression
module type and type equations
lexical implicit-module space
SI implicit functions
ordinary value
expected value type
rightmost eligible lexical binding
All three elaborate omission into explicit target syntax. That shared last step is not enough to identify their coherence, termination, ambiguity, or abstraction theorems. A theorem transfers only through an explicit translation preserving the hypotheses that make resolution meaningful.
Sources and exact limits.
The modular-type-class development, including its declarative elaboration and inference-soundness theorem, is due to Dreyer, Harper, Chakravarty, and Keller [DHCK07]. Modular implicits and their archived OCaml prototype are due to White, Bour, and Yallop [WBY15]; their paper presents the design and elaboration but not a complete formal inference/coherence metatheory for OCaml. The SI calculus and the exact results in theorem 16.17 are due to Odersky, Blanvillain, Liu, Biboudis, Miller, and Stucki [OBL^+18]. Stable coherent implicits provide a useful neighboring calculus, but its theorems require its own stability and unambiguity conditions and are not silently inherited here [SdSOWM19].
★★☆ For this exercise, extend 𝖬𝖳𝖢0 as follows. A class may contain finitely many named associated type components in addition to its distinguished carrier 𝑡; every declaration gives each component a transparent constructor expression. Resolution is still keyed only by the class and carrier head (𝐾,𝑐), so non-overlap is unchanged, while field selection recovers the associated equations from the selected module. Use the extended class signature 𝖢𝖮𝖫𝖫𝖤𝖢𝖳𝖨𝖮𝖭:=𝗌𝗂𝗀{𝗍𝗒𝗉𝖾𝑡;𝗍𝗒𝗉𝖾𝖤𝗅𝖾𝗆;𝗏𝖺𝗅𝖾𝗆𝗉𝗍𝗒:𝑡;𝗏𝖺𝗅𝗂𝗇𝗌𝖾𝗋𝗍:𝖤𝗅𝖾𝗆→𝑡→𝑡}. Give modules for integer lists and integer sets that have the same associated component 𝖤𝗅𝖾𝗆=𝖨𝗇𝗍 but different carrier types. Explain why the class cannot be represented by the single dictionary type 𝑡→𝑡→𝖡𝗈𝗈𝗅 used for equality in chapter 11. Then state the realized signatures of both modules and show how transparent access to 𝖤𝗅𝖾𝗆 type-checks one insertion call for each.
★★★ Prove the converse limitation of corollary 16.7: if an extension is permitted to replace the declaration at a result head occurring in an old evidence derivation, resolution need not be stable. Give the smallest counterexample. Then, for finite admissible environments viewed as maps from result heads to declarations, formulate and prove a stronger stability statement in which an update may rewrite entries at heads outside the old derivation, provided every entry used by that derivation remains syntactically identical.
★★☆ A modular-implicit search space contains 𝖮𝗋𝖽𝖳𝗈𝖤𝗊,𝖧𝖺𝗌𝗁𝖳𝗈𝖤𝗊,𝖮𝗋𝖽𝖨𝗇𝗍,𝖧𝖺𝗌𝗁𝖨𝗇𝗍. The first two are functors; the latter two provide ordering and hashing for integers. Both functor applications produce modules matching 𝖤𝖰 with carrier 𝖨𝗇𝗍. Draw the two evidence paths for a call requiring integer equality. State one additional premise under which alias equivalence could collapse the paths, and explain why extensional agreement of the two 𝖾𝗊 functions alone is not a decidable module-alias test.
★★★ The uniqueness propositions imported for SI exclude polymorphic function types. Construct two distinct typing and elaboration choices made possible by a polymorphic implicit value, or reconstruct the counterexample from the source. Identify the exact step at which the monomorphic uniqueness induction no longer determines one restricted synthesized type. Your answer must separate failure of uniqueness from possible divergence of the search algorithm.
★★☆ For each requirement below, select 𝖬𝖳𝖢0/modular type classes, modular implicits, SI implicit functions, or explicit module passing, and justify the choice from the formal search and scope rules rather than surface syntax.
two local pretty-printing configurations for the same carrier, selected at different call sites;
an associated output type that must remain abstract behind a module boundary;
a lexically shadowed integer tolerance used by ordinary functions;
a security-sensitive dependency for which no search or ambiguity is acceptable; and
automatic structural construction of equality evidence for nested products and lists with one available instance per constructor head.
For one item, give a plausible second choice and a concrete reason it is worse.
★★★Practical project.mtc-resolution Implement the four-constructor running sublanguage of the finite calculus in definition 16.2 and the resolver of definition 16.5. The algorithm must validate declarations before search, then perform head-directed recursive resolution. Maintain this invariant: every recursive request is at a proper type subterm of its caller, and every returned evidence tree type-checks against the requested realized signature.
The concrete observable result is a command-line report containing validation, resolution, and elaboration outcomes. The decidable acceptance test must check all of the following exact cases:
the standard environment is accepted;
resolving the request 𝖤𝖰[𝖫𝗂𝗌𝗍(𝖯𝖺𝗂𝗋(𝖨𝗇𝗍,𝖡𝗈𝗈𝗅))] prints 𝙴𝚚𝙻𝚒𝚜𝚝(𝙴𝚚𝙿𝚊𝚒𝚛(𝙴𝚚𝙸𝚗𝚝,𝙴𝚚𝙱𝚘𝚘𝚕));
elaborating equality at that type prints the same evidence followed by .eq;
adding a second 𝖤𝖰[𝖨𝗇𝗍] declaration is rejected with overlap;
omit the declaration for 𝖲𝖧𝖮𝖶[𝖡𝗈𝗈𝗅]; resolving that request is rejected with missing; and
the declaration 𝖤𝗊𝖠𝗀𝖺𝗂𝗇:𝖤𝖰[𝛼]⇒𝗆𝗈𝖽𝖤𝖰[𝛼] is rejected with nondecreasing.
The inline kappa test oracle must exit unsuccessfully if any expected output differs; kappa run prints the diagnostic report and is not the process-level rejection oracle. The companion directory is