Lectures onType Theory
ch:modular-type-classes: ch:modular-type-classes
appendix sectionsolutions

ch:modular-type-classes: ch:modular-type-classes

Exercise 16.1.

The monomorphic choice fixes the available module at the definition site: A.fλx:Int.EqInt.eq(x,x+2). Hence B.yEqInt.eq(3,5)=false. The constrained-polymorphic choice exports the evidence parameter: A.fΛX:EQ.λx:X.t.X.eq(x,x+2). At the use in B, inference may instantiate X with EqParity, giving B.yEqParity.eq(3,5)=true. The boundary of A must decide whether f is exported at the monomorphic signature IntBool or at the constrained-polymorphic signature X:EQ.X.tBool. Instance scope alone cannot determine that exported interface.

Exercise 16.2.

The evidence tree is ShowListShowPairShowBool,ShowInt. Its proper evidence subterms have types ShowBool:SHOW[Bool],ShowInt:SHOW[Int],ShowPairShowBool,ShowInt:SHOW[Pair(Bool,Int)]. Applying ShowList to the last subterm gives ShowListShowPairShowBool,ShowInt:SHOW[List(Pair(Bool,Int))].

Exercise 16.3.

The two accepted declarations have result heads (SHOW,Int)and(SHOW,List). Both heads are absent from the original environment, so lemma 16.4 applies to ShowInt and to ShowList.

For EqNever, the result head is (EQ,Int), already occupied by EqInt. The non-overlap premise fails. If the adoption were allowed, the same request would have two derivations, ΘEQ[Int]resEqIntandΘEQ[Int]resEqNever, with observably different equality operations.

Exercise 16.4.

Depth-first, left-to-right resolution visits these keys: (EQ,List(Pair(Int,Bool))),(EQ,Pair(Int,Bool)),(EQ,Int),(EQ,Bool). The first three lookups find EqList, EqPair, and EqInt. The fourth finds no declaration, so the first reported error is missing(EQ,Bool). There is no possible R-Base conclusion for the Boolean request. Thus the second premise needed for the R-Functor application of EqPair cannot be completed; the enclosing EqList premise consequently fails as well.

Exercise 16.5.

Put V:=ShowListShowPairShowBool,ShowInt. Resolution gives ΘSHOW[List(Pair(Bool,Int))]resV. The source elaborates to λz:List(Pair(Bool,Int)).V.show z. Let Γz:=z:List(Pair(Bool,Int)). Evidence typing gives ΘV:SHOW[List(Pair(Bool,Int))]. Target weakening therefore gives the same judgment under Γz;Θ, and signature projection gives Γz;ΘV.show:List(Pair(Bool,Int))String. The variable rule types z in Γz;Θ, so application has type String. Abstraction discharges z 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 ShowList. Its premise is the pair request, which selects ShowPair. The first pair premise is the variable request SHOW[α], so retain X:SHOW[α]. The second premise is a list request at Int, solved by ShowListShowInt. Reduction therefore returns (X:SHOW[α],ShowListShowPairX,ShowListShowInt). Grounding α to Bool and substituting ShowBool for X gives ShowListShowPairShowBool,ShowListShowInt, which has the realized signature SHOW[List(Pair(Bool,List(Int)))].

Exercise 16.7.

  1. Yes. This is the theorem’s ground top-level consequence.

  2. No. This is a completeness claim. The published theorem is one-way soundness, and the calculus intentionally inherits incompleteness from ML module inference.

  3. No. This is a coherence claim comparing successful elaborations. Type preservation of each elaboration does not establish their observational equivalence.

  4. 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 M by M.t=Int list. Trying either list functor removes one list constructor and generates the argument constraint S.t=Int, solved by ShowInt. The two complete candidates are therefore ShowList(ShowInt)andCompactShowList(ShowInt). 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 i10, the fresh parameter introduced while checking add by ia, and the inner implicit integer by i3. Checking the declared implicit-function type introduces ia before checking the body, so the query in x+? selects that rightmost eligible variable: addλia:Int.λx:Int.x+ia. The later inner let is not in lexical scope at this definition and cannot be observed by that query.

At the use of add, automatic implicit elimination generates a new query at Int. Here i3 is the rightmost eligible binding, so add 4(λia.λx.x+ia) i3 44+i3. Substituting the inner binding yields 4+3=7. The outer value 10 is shadowed at the call site.

Exercise 16.10.

Take IntLists:COLLECTION[t=List(Int),Elem=Int],IntSets:COLLECTION[t=IntSet,Elem=Int]. The first module may define empty=[] and insert=cons; the second may define the empty finite set and its duplicate-removing insertion operation. Their realized signatures are IntLists:sig {type t=List(Int);type Elem=Int;val empty:t;val insert:Elemtt}, and IntSets:sig {type t=IntSet;type Elem=Int;val empty:t;val insert:Elemtt}. Consequently both IntLists.insert 3 IntLists.emptyandIntSets.insert 3 IntSets.empty are well typed at their different carrier types. The equality dictionary ttBool contains neither an associated element type nor the empty and insert 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 Θ={EqInt:EQ[Int]}. It resolves integer equality to EqInt. If an extension is allowed to replace that head with EqNever:EQ[Int], the same request resolves to EqNever. 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 ΘK[τ]resV, 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 ΘK[τ]resV, and its deterministic resolver returns V.

Prove this by induction on the derivation of V. 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 V. 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 OrdToEq(OrdInt)andHashToEq(HashInt), and both match EQ with carrier Int. 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 OrdToEq(OrdInt)=EqInt=HashToEq(HashInt) in the module language’s decidable path-equivalence relation. Merely knowing that the two eq 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, i:Int,b:Bool,y:X.X?Int, and ultimately check a query at Int. In both derivations, SI-Query first synthesizes the type of the same rightmost implicit variable y, so lexical well-scopedness does not distinguish them. SI-AllE can nevertheless choose X=Int, after which SI-ImpE supplies i and SI-Stitch reaches the checking judgment, yielding y[Int] i:Int, or SI-AllE can choose X=Bool, after which SI-ImpE supplies b, yielding y[Bool] b:Int. 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.

  1. 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 SHOW module type.

  2. 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.

  3. Use SI implicit functions. The tolerance is an ordinary value selected by expected type and the rightmost lexical binding, exactly the required shadowing behavior.

  4. 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.

  5. Use MTC0 or the full modular-type-class discipline. The pair (EQ,c) 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 tString. 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.

Search the book

Type to search the local edition.