Uniwersytet Warszawski

Wydział Matematyki, Informatyki i Mechaniki

Université de Paris-Sud

Jacek Chrząszcz

Modules in Type Theory with Generative Definitions

Supervisors:

Professor Jean-Pierre Jouannaud Professor Paweł Urzyczyn

Warsaw, September 2003 Pracę niniejszą dedykuję mojej Mamie i jej marzeniom.

Acknowledgments

First of all, I would like to thank my supervisors, Jean-Pierre Jouannaud and Paweł Urzyczyn. My French supervisor, Jean-Pierre Jouannaud, gave me this interesting subject and guided my work through the years. Without his invaluable help in all aspects of scientific life, this thesis would not have been possible.

I am also very grateful to Paweł Urzyczyn, my Polish supervisor, who brought me to the research area of the λ-calculus. He was a great source of encouragement and support from the very beginning of our collaboration.

During my visits in France, I had many occasions to present and discuss my cloudy thoughts about marying rewriting and modules. I would like to particularly thank Christine Paulin, Gilles Dowek, Judicaël Courant and Frédéric Blanqui for their active part in these discussions and their multiple suggestions.

The practical result of my thesis — implementation of modules in Coq — could not have been done without help and many useful explanations of Coq developers: Hugo Herbelin, Jean-Christophe Filliâtre and Bruno Barras. I am also grateful to Pierre Letouzey for his extraction of modules.

The final result of the many years of my work would not have been possible without help of my wife Daria, who made everything she could to make this dream come true. She was the only person who listened to every detail of every proof contained in this dissertation.

I thank the members of research groups: ZLS in Warsaw, Démons in Orsay and Logical in Roquencourt for their encouragements and a motivating work environment.

I gratefully acknowledge the financial support of the French government and Polish KBN grant no 4 T11C 024 22.

Contents

1 Introduction 7 1.1 Formal proofs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.2 The calculus of constructions . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.3 Inductive types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.4 Rewriting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 1.5 Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

1.5.1 Informal presentation . . . . . . . . . . . . . . . . . . . . . . . . . . 18 1.5.2 Module system for type theory . . . . . . . . . . . . . . . . . . . . 20

1.6 Anonymous modules, inductive types and rewriting . . . . . . . . . . . . . 27 1.6.1 Blocks and their typing . . . . . . . . . . . . . . . . . . . . . . . . . 30 1.6.2 Characteristics of the calculus . . . . . . . . . . . . . . . . . . . . . 32 1.6.3 Closure properties for rewriting . . . . . . . . . . . . . . . . . . . . 36

1.7 Named modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 1.8 Contributions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 1.9 Roadmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

2 Named modules 40 2.1 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 2.2 Judgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 2.3 Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 2.4 Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 2.5 Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 2.6 Specifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 2.7 Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

2.7.1 Closure properties . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 2.8 Convertibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 2.9 Positions, subexpressions and bound variables . . . . . . . . . . . . . . . . 51

3 Conservativity roadmap 55 3.1 Conservativity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 3.2 Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 3.3 Flattening . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 4 Basic metatheory 62

4.1 General properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 4.1.1 Substitutivity of paths . . . . . . . . . . . . . . . . . . . . . . . . . 64 4.1.2 Module types, subtyping and convertibility . . . . . . . . . . . . . . 67

4.2 Inversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

5 Principal typing 79 5.1 Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 5.2 Specifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 5.3 Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 5.4 Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 5.5 Convertibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 5.6 Principal module types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 5.7 Properties of principal derivations . . . . . . . . . . . . . . . . . . . . . . . 85 5.8 Completeness of principal derivations . . . . . . . . . . . . . . . . . . . . . 88 5.9 Module elimination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 5.10 Correctness of types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

6 Module evaluation 99

6.1 Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 6.1.1 Structure prefixes and suffixes . . . . . . . . . . . . . . . . . . . . . 99 6.1.2 Value of a path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 6.1.3 The evaluation function . . . . . . . . . . . . . . . . . . . . . . . . 102

6.2 Type preservation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 6.3 Termination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108

6.3.1 Translation into WMC . . . . . . . . . . . . . . . . . . . . . . . . . 108 6.3.2 Correctness of the translation . . . . . . . . . . . . . . . . . . . . . 111 6.3.3 Computation is a delta-mu reduction . . . . . . . . . . . . . . . . . 116 6.3.4 Shape positions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 6.3.5 Delta-force reduction . . . . . . . . . . . . . . . . . . . . . . . . . . 122 6.3.6 Counting positions . . . . . . . . . . . . . . . . . . . . . . . . . . . 125

7 Structure flattening 130 7.1 Environments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 7.2 Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

8 Decidability 137 8.1 Principal derivations of inductive types . . . . . . . . . . . . . . . . . . . . 137 8.2 Elimination of manifest modules . . . . . . . . . . . . . . . . . . . . . . . . 140 8.3 Removing modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 8.4 Removing manifest blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 8.5 Relative decidability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 9 Implementation in Coq 154

9.1 The architecture of Coq . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 9.2 Modules in the kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155

9.2.1 Interactive modules . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 9.2.2 Overview of the environment structure . . . . . . . . . . . . . . . . 158 9.2.3 Avoiding name clashes . . . . . . . . . . . . . . . . . . . . . . . . . 160 9.2.4 Modules and sections . . . . . . . . . . . . . . . . . . . . . . . . . . 162 9.2.5 The with notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 9.2.6 Implementation details . . . . . . . . . . . . . . . . . . . . . . . . . 163

9.3 Modules outside the kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 9.3.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 9.3.2 Module operations on objects . . . . . . . . . . . . . . . . . . . . . 173 9.3.3 Interactive modules revisited . . . . . . . . . . . . . . . . . . . . . . 175 9.3.4 Non-interactive modules . . . . . . . . . . . . . . . . . . . . . . . . 176 9.3.5 Long names and suffix visibility . . . . . . . . . . . . . . . . . . . . 178 9.3.6 Summary of library files . . . . . . . . . . . . . . . . . . . . . . . . 179

9.4 Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181

10 Related work 182 10.1 Module systems for ML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 10.2 Theory management in proof assistants . . . . . . . . . . . . . . . . . . . . 183

10.2.1 A module system for ELF . . . . . . . . . . . . . . . . . . . . . . . 183 10.2.2 PVS theories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183 10.2.3 Maude’s module algebra . . . . . . . . . . . . . . . . . . . . . . . . 183 10.2.4 Isabelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 10.2.5 Dependent records . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 10.2.6 Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185

11 Satisfying closure properties 186

12 Conclusions and future work 192 12.1 Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 12.2 Future work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193

Chapter 1

Introduction

Program correctness has been considered as a major area for research from the early days of computer science. Because computers play now a central role in our daily life, the need for ensuring that programs behave according to their specification has become a major practical concern.

Because searching for a proof of a proposition is undecidable, program correctness was considered a dream until recently. Proving correctness of very large programs is still out of reach of current technologies. However, proving correctness of chips or critical applications like security protocols or telecommunication protocols is now possible. Indeed, tools exist that allow proving correctness of medium size programs.

The most widely used measure to make sure that a computer program is correct is testing. Unfortunately, as noticed by Dijkstra, “Testing can only show the presence of errors, never their absence.” The same thing, to a certain extent, can be said about automatic verification tools. If the tool does not find any errors, what does convince us that the program is correct?

In a proof assistant, the user builds a proof with the help of the computer which takes care of the many tedious details as well as of the proof bookkeeping. The proof assistant will convert the user’s indications into a formal proof that will then be checked by the system, using the fact that, contrary to proof search, proof checking is decidable. Besides, the formal proof can be used as a correctness certificate and verified by an independent checker, therefore answering the previous question.

Unfortunately, using any of the existing proof assistants is still quite hard. It is necessary to give very precise hints to the system and things that are obvious for humans often require hours of work to be formally stated and proved. Besides, tools for structuring large proofs are not satisfactory, making proof reuse difficult.

The present thesis addresses this question. We describe a module system similar to those used in programming languages, well suited to proof assistants based on type theory. It permits breaking the development of large proofs into smaller pieces that can later be put together by using composition and instantiation.

This module system is implemented in the Coq proof assistant. It fits a type theory extended with generative definitions, such as inductive types and rewriting, therefore anticipating future versions of Coq: while inductive types have been present for over ten years in Coq, rewriting is still an active research topic, the implementation of which has started only recently.

As any extension of the type theory used for representing and checking proofs, the present one must be proved sound. In this thesis, soundness is obtained relatively to the previously used type theory: it is shown that adding our module system is conservative, that is, does not allow proving propositions that could not have been proved before. In other words, it is possible to obtain new proofs of a given proposition, but these new proofs can indeed be recursively transformed into old ones.

The version 7.4 of Coq with modules can be downloaded from the Coq site. Although this new feature is announced as experimental, it is used by the most heavy users of the system. Some important developments taking advantage of modules have already been realized.

1.1 Formal proofs

Proof assistants are interactive programs, which help users write formal mathematics. They implement a certain logical calculus specifying how to formalize mathematical notions, theorems and proofs, and make sure that every user’s input is correct with respect to the underlying logic.

Proof assistants provide their users with a set of procedures called tactics which construct pieces of proofs. Tactics can either be general, like trying to apply any theorem that has already been proved, or specific to a given domain, like proving equalities in group theory or proving instances of propositional tautologies.

A major role of a proof assistant is to make sure that none of the tedious details of a proof is forgotten.

Depending on the status of proofs in the implemented logic and the paradigm on which the proof assistant is built, the proof produced using tactics is either immediately accepted as correct or is reverified by a proof-checker independent from the tactics.

In PVS [88, 78], a proof constructed by the tactics is automatically accepted as correct. Writing tactics is straightforward: given a mathematical statement, the user can write a tactic which answers true/false and no other justification is required by the system.

In Isabelle [52, 82], the proof constructed by tactics is also accepted without rechecking, but in order to construct an object that is accepted as a proof, one has no other way but using tacticals built from basic tactics, which boils down to building a complete proof from the basic tactics themselves, which happen to be derivation rules and axioms of the underlying logic.^{1}

In Coq [23, 24], a proof assistant based on the so-called Curry-Howard isomorphism, a proof is an object of the underlying logical formalism, and upon completion, is reverified by a type-checker, called the kernel of the proof assistant. In this case, the correctness of a proof depends only on the correctness of the kernel. Since the kernel is rather small in practice, easy to read, and can be itself proved correct, this technology is inherently more reliable than any other.

1Recently a possibility of using decision procedures has been added to Isabelle, which also allows for accepting theorems without any justification.

Curry-Howard isomorphism. In 1958 Curry [37] noticed that typing derivations in simply typed lambda calculus correspond to natural deduction proofs in minimal propositional logic (i.e. using only implication). Moreover, in 1965 Tait [95] saw the correspondence between the computation rule of the lambda calculus, and cut elimination which simplifies the proof obtained by the introduction of an implication followed immediately by its elimination.

More precisely, it turned out that normal proofs (without cuts) correspond to lambda terms in normal form and that the cut elimination theorem corresponds to normalization property of the simply typed lambda calculus. Quite a few more logics have their lambda calculus counterparts. For example, second-order propositional logic corresponds to Girard’s system F of polymorphic types [45] and first order logic can be encoded using dependent types.

This phenomenon is called the Curry-Howard isomorphism: it establishes the correspondence between propositions and types, proof and terms, provability and inhabitation, proof normalization and term reduction.

In a proof assistant based on the Curry-Howard isomorphism, such as Coq, stating a theorem amounts to writing a well-formed type, and proving this theorem is nothing else but finding a term, whose type corresponds to the theorem in question.

1.2 The calculus of constructions

In the late 80s, it was noticed that many known type systems for lambda terms differ only in the allowed functional types [7, 96, 4, 5]. A unified syntax and parameterized typing rules was proposed to cover eight type systems, called together the lambda cube.

The syntactic class of pseudoterms, denoted by the letters e and t to give an intuitive distinction between terms and types, is defined as follows:

e, t ::= v | (e_{1} e_{2}) | [v:t] e | (v:t_{1}) t_{2} | s
s ::= ⋆ | □

A pseudoterm is either a variable, an application of two pseudoterms, an abstraction, a product, or a sort denoted by the letter s. Sorts ⋆ and □ are two constants. We write abstraction and product in Coq ASCII notation [v:t] e and (v:t_{1}) t_{2}, which corresponds to the standard λv:t.e and Πv:t_{1}.t_{2}, respectively.

The typing rules tell us how to derive types for terms in a given environment. We will divide them into two parts. The first part deals with the environment, which (in this section) is a sequence of pairs made of a variable and a pseudoterm.

\underline{Empty}  \overline{ϵ ⊢ ok}
\underline{Insert}  \frac{E ⊢ ok E ⊢ t : s}{E; v : t ⊢ ok}  \underline{Lookup}  \frac{E_{1}; v : t; E_{2} ⊢ ok}{E_{1}; v : t; E_{2} ⊢ v : t}

Intuitively, an environment assigns types to variables, and the judgment E ⊢ ok means that E is a correct environment.

The second set of rules deals with typing judgments. Note that the Product rule is parametrized by the set C, such that (⋆, ⋆) ∈ C ⊆ {(⋆, ⋆), (□, ⋆), (⋆, □), (□, □)} and that ≈ in the Conversion rule means β-convertibility, i.e. the congruence generated by the beta reduction rule [v:t]e e′ ⟶_{β} e{v ↦ e′}.

\underline{Axiom}  \frac{E ⊢ ok}{E ⊢ ⋆ : □}

\underline{Product} \frac{E ⊢ t_{1} : s_{1} E; v : t_{1} ⊢ t_{2} : s_{2}}{E ⊢ (v:t_{1}) t_{2} : s_{2}} where (s_{1}, s_{2}) ∈ C

\underline{Abstraction}  \frac{E; v : t_{1} ⊢ e : t_{2} E ⊢ (v:t_{1}) t_{2} : s}{E ⊢ [v:t_{1}]e : (v:t_{1}) t_{2}}
\underline{Application}  \frac{E ⊢ e : (v:t_{1}) t_{2} E ⊢ e′ : t_{1}}{E ⊢ e e′ : t_{2}\{v ↦ e′\}}
\underline{Conversion}  \frac{E ⊢ e : t E ⊢ t′ : s E ⊢ t ≈ t′}{E ⊢ e : t′}

The sort ⋆ corresponds to the type of all types (or propositions) and the sort □ to the type of all kinds, ⋆ being the simplest kind expression. The choice of the set C determines what products^{2} can be formed in the calculus. Since (⋆, ⋆) is always an element of C, the product of two types is always a type. Other pairs in C yield the following characteristics:

• (□, ⋆) corresponds to polymorphism;

• (⋆, □) corresponds to dependent types;

• (□, □) corresponds to type constructors.

The Coq proof assistant is based on Coquand and Huet’s calculus of constructions [29], the most complex typed lambda calculus of the lambda cube.^{3} It has the three above possibilities, making it a formalism capable of encoding higher-order intuitionistic predicate logic. Lambda terms typable in the calculus of constructions can represent all functions provably total in higher-order arithmetics, a property shared with another calculus of the lambda cube due to Girard, called F^{ω} [45], which does not have dependent types.

2Note that product means here a dependent function space rather that a Cartesian product.

3The presentation of typing rules with Insert and Lookup follows [85, 28, 29] rather then the definition from [5], where Var and Weak rules are used.

Examples. We will now give a couple of examples illustrating various features of the calculus of constructions. In the examples, we will use the syntax A → B to denote a product (v:A) B, where v does not occur in B.

According to the Curry-Howard isomorphism, logical propositional variables correspond to the variables of sort ⋆ in the calculus of constructions. Given a variable A, one may prove that A → A is a correct proposition, i.e:

A : ⋆ ⊢ A → A : ⋆

In order to do this, one uses the Product rule for sorts (⋆, ⋆). Moreover, it is possible to prove this proposition:

A : ⋆ ⊢ [x:A] x : A → A

Using the Product rule for sorts (□, ⋆), corresponding to polymorphism, one can prove that (A:⋆)A is a correct proposition. It corresponds to the false proposition, unprovable in the calculus of constructions, i.e. there is no term t such that ⊢ t : (A:⋆)A. On the other hand (A:⋆)A really corresponds to a false proposition, as in any environment E and any proposition P (i.e. a term such that E ⊢ P : ⋆), we can prove that (A:⋆)A implies P. Indeed:

E ⊢ [f : (A:⋆)A](f P) : ((A:⋆)A) → P

Using polymorphism one can also encode logical connectives such as conjunction or disjunction. For example, given two propositional variables A and B, the term (C:⋆)(A → B → C) → C corresponds to the proposition A ∧ B: it is possible to construct a proof of A ∧ B from a proof of A and a proof of B and vice-versa.

This encoding allows to form the conjunction of two propositions, but it turns out that polymorphism is not enough to construct the conjunction operator itself. Indeed, if and is supposed to be a term such that E ⊢ (and P Q) : ⋆, then E ⊢ and : ⋆ → ⋆ → ⋆. But the latter is only typable (by a sort □) if the Product rule (□, □) is allowed. In the calculus of constructions the term and is simply

[A : ⋆][B : ⋆](C:⋆)(A → B → C) → C

The Product rule for (⋆, □) is required to form predicates. Given a type A (i.e. a term A of type ⋆), every term P of type A → ⋆ corresponds to a unary predicate over A. A binary predicate of Leibniz equality on the type A can be obtained as the following term of type A → A → ⋆:

[a : A][b : A](P : A → ⋆)(P a) → (P b)

It becomes also possible to encode datatypes, such as natural numbers, booleans and lists, using the so called impredicative encoding. For example, the type of natural numbers is encoded as follows:

NAT ≡ (C:⋆)C → (C → C) → C
0 ≡ [C:⋆][z:C][s:C → C]z
succ ≡ [n : NAT ][C:⋆][z:C][s:C → C](s (n C z s))

Using this encoding, one can define recursive functions, like addition:

plus ≡ [n : NAT ][m : NAT ] (n NAT m succ)

using the numbers themselves as polymorphic recursors.

Beyond the calculus of constructions. The concept of the λ-cube has been further generalized to the so-called pure type systems, or PTS for short [44]. The typing system presented for the λ-cube is parameterized not only by the set of acceptable products, but by a triple consisting of a set of sorts S, a binary relation A and a ternary relation R over sorts. The latter two govern the modified typing rules Axiom and Product:

\underline{Axiom} \frac{E ⊢ ok}{E ⊢ s_{1} : s_{2}} if (s_{1}, s_{2}) ∈ A

\underline{Product} \frac{E ⊢ t_{1} : s_{1} E; v : t_{1} ⊢ t_{2} : s_{2}}{E ⊢ (v:t_{1}) t_{2} : s_{3}} if (s_{1}, s_{2}, s_{3}) ∈ R

All other typing rules are unchanged.

Many interesting logical systems can be presented using the pure type system formalism. These include several systems from the AUTOMATH family [97, 38], higher-order logic [20] and many others. The type system implemented in Coq is based on a PTS, where the sort ⋆ is split into two: ⋆_{P} and ⋆_{S} (which are denoted in Coq as Prop and Set respectively) to separate the universe of propositions from the universe of types. On the other hand, there is a whole hierarchy of □_{i} for all i ∈ ℕ called the predicative universe hierarchy. The axioms are A = {⋆_{P} : □_{i}, ⋆_{S} : □_{i}, □_{i} : □_{j} for any i < j}, and product rules {(⋆_{a}, ⋆_{b}, ⋆_{b}), (□_{i}, ⋆_{b}, ⋆_{b}), (⋆_{a}, □_{i}, □_{i}), (□_{i}, □_{j}, □_{k}) for any ⋆_{a}, ⋆_{b} = ⋆_{P} , ⋆_{S}, i, j ≤ k}^{4}.

Definitions. As we can observe from the examples above, formalizing mathematical notions in a readable form requires the possibility to abbreviate terms by names. This facility was introduced in Coq in the very beginning (already in its precursor called CoC) in the form of global definitions formalized by extending the capabilities of the environment: besides the usual entries of the form v : t assigning types to variables, new entries of the form v : t := e assign a type t and a value e to names of constants. The corresponding typing rules are the following:

\underline{Insert} \frac{E ⊢ ok E ⊢ e : t}{E; v : t := e ⊢ ok}   \underline{Lookup} \frac{E_{1}; v : t := e; E_{2} ⊢ ok}{E_{1}; v : t := e; E_{2} ⊢ v : t}

In order to use the definitions, one defines the new reduction rule δ:

E_{1}; v : t := e; E_{2} ⊢ v ⟶_{δ} e

4In fact the type theory of Coq contains also a cumulativity rule allowing to derive t : □_{j} from t : □_{i} for any j > i, which does not match the PTS formalism [26, 64].

and consequently the relation ≈ used in the conversion rule becomes βδ-equality. Pure type systems with definitions have been formally studied in [93].^{5}

Unlike regular variables which be can abstracted, definitions are global and cannot be removed once they have been introduced in the environment. Consequently, a variable declaration of the form v : t which appears in the environment before some definitions, cannot be removed either. One can say that such a variable becomes a global axiom or a global parameter.

Coq concrete syntax for definitions, axioms and parameters is the following:^{6}

      Definition Id : (T:Set)(T→T) := [T:Set][x:T]x.
      Axiom a : A.
      Parameter A : Prop.

1.3 Inductive types

Impredicative encodings as they have been presented in the previous section have many drawbacks both on the usability and logical side: it is quite counter-intuitive to write functions on encoded datatypes; some easy functions cannot be written efficiently, for example the predecessor function on natural numbers performs in linear instead of constant time; it is not possible to prove that 0 ≠ 1; and the induction principle itself is not provable.

Anonymous inductive definitions. For all these reasons primitive inductive types have been added to Coq in version 5.6. Inductive types are packages containing names and types of (mutually defined) inductive types and their constructors. This is denoted by two environments E^{I} = v_{1}^{I} : t_{1} … v_{k}^{I} : t_{k} (for inductive types) and E^{C} = v_{1}^{c} : t̂_{1} … v_{n}^{c} : t̂_{n} (for constructors), and the whole inductive package is written as Ind(E^{I} := E^{C}).

The first typing rule defines when a package representing an inductive definition is correct.

\underline{Formation} \frac{E ⊢ t_{j} : s_{j} for j = 1 … k E; E^{I} ⊢ t̂_{i} : s′_{i} for i = 1 … n}{E ⊢ Acceptable(Ind(E^{I} := E^{C}))} if POS_{E}(E^{I} := E^{C})

The side condition POS_{E}(E^{I} := E^{C}), called positivity condition, is a syntactic condition on the kinds of inductive types and types of constructors. Informally, the kind of an inductive type must be a product whose co-domain is a sort, and the type of a constructor must be a product, whose co-domain is one of the inductive types, and whose domain can contain only positive occurrences of the inductive types. Precise formulation of these conditions differs slightly in various papers [80, 81, 100, 24] and since we are not interested in a very precise analysis of inductive types, we do not quote any of them.

5In fact the authors studied not only global but also local definitions.

6The type of the definition can also be synthesized automatically.

A correct inductive package allows to introduce new kinds of terms of the form Ind(E^{I} := E^{C}).i which are used to represent the inductive types themselves (for i ≤ k) and their constructors (for i > k). Here are the corresponding typing rules:

\underline{Inductive Type} \frac{E ⊢ Acceptable(Ind(E^{I} := E^{C}))}{E ⊢ Ind(E^{I} := E^{C}).i : t_{i}} if i ≤ k

\underline{Constructor} \frac{E ⊢ Acceptable(Ind(E^{I} := E^{C}))}{E ⊢ Ind(E^{I} := E^{C}).i : t̂_{i−k}\{v_{j}^{I} ↦ Ind(E^{I} := E^{C}).j\}_{j=1...k}} if i > k

Note the substitution in the conclusion of the second rule: the type t̂_{i−k} may contain free occurrences of v_{1}^{I} … v_{k}^{I}, which were previously bound in Ind(E^{I} := E^{C}).

One can now define natural numbers in Peano style, with E^{I} = nat : ⋆ and E^{C} = O : nat; S : nat → nat. This definition satisfies the positivity condition, and therefore E ⊢ Acceptable(Ind(E^{I} := E^{C})).

In order to conveniently use natural numbers, we need to give names to the type itself and its constructors, using global definitions as described in the previous section:

nat : ⋆ := Ind(E^{I} := E^{C}).1
O : nat := Ind(E^{I} := E^{C}).2
S : nat → nat := Ind(E^{I} := E^{C}).3

Apart from the typing rules for inductive types and their constructors (the latter are also called introduction rules), there are also elimination rules for inductive types. For every j = 1 … k the term Elim(Ind(E^{I} := E^{C}), j) denotes the eliminator for the j-th inductive type defined in the inductive package. These terms correspond to recursors from the computation point of view and to induction principles from the logical point of view. The corresponding reduction rules are called the ι-rules. Unfortunately, the typing schemes for eliminators are very complicated. Since we will not need them in the rest of the thesis (eliminators can be directly defined by rewriting), we will omit the details here. Instead, we simply give the dependent recursor for the inductive type of natural numbers given above:

Elim(Ind(E^{I} := E^{C}), 1)

: (P : nat → ⋆)(P O) → ((n : nat)(P n) → (P (S n))) → (n : nat)(P n)

Its associated reduction rules, after abbreviating Elim(Ind(E^{I} := E^{C}), 1) by nat_{rec}, are the following:

nat_{rec} P f_{z} f_{s} O ⟶_{ι} f_{z}
nat_{rec} P f_{z} f_{s} (S n) ⟶_{ι} f_{s} (nat_{rec} P f_{z} f_{s} n)

Named inductive definitions. The inductive definitions presented so far are anonymous. Creating an inductive definition and giving names to its components come separately. This approach follows the λ-calculus tradition of describing functions without giving them names in the first place, and this approach is indeed well suited to a theoretical analysis of inductive types.

From a practical point of view however, anonymous inductive types are very cumbersome since even simple terms containing explicit constructors are very big and therefore unreadable. Until version 6.2 of Coq, abbreviations of anonymous inductive types were not unfolded unless really necessary, but this made the implementation quite complicated and did not always work.

The implementation of inductive definitions in Coq was changed in version 6.2. An inductive definition written by the user became a declaration of names corresponding to the inductive type and its constructors, and the information binding them together in an inductive package appeared in the environment instead of in the terms themselves. This corresponds to the following typing rules:

\underline{Insertion} \frac{E ⊢ ok E ⊢ t_{j} : s_{j} for j = 1 … k E; E^{I} ⊢ t̂_{i} : s′_{i} for i = 1 … n}{E; IndDef(E^{I} := E^{C}) ⊢ ok} if POS_{E}(E^{I} := E^{C})

Inductive Type \frac{E_{1}; IndDef(E^{I} := E^{C}); E_{2} ⊢ ok}{E_{1}; IndDef(E^{I} := E^{C}); E_{2} ⊢ v_{j}^{I} : t_{j}}

\underline{Constructor}  \frac{E_{1}; IndDef(E^{I} := E^{C}); E_{2} ⊢ ok}{E_{1}; IndDef(E^{I} := E^{C}); E_{2} ⊢ v_{i}^{c} : t̂_{i}}

Note that there is no more need for a substitution in the Constructor rule: the inductive type names occurring in t̂_{i} are now valid in the global environment.

Another difference is that named inductive definitions are generative: in an environment containing the inductive definition of natural numbers

IndDef( nat : ⋆ := O : nat; S : nat → nat )

it is not possible to give a definition of type nat without mentioning the name nat. In other words, given an environment E, there is no term t typable in E such that E; IndDef(E^{I} := E^{C}) ⊢ t ≈ nat, where IndDef(E^{I} := E^{C}) is the definition of natural numbers.

The concrete Coq syntax for inductive types is very similar to the formal one. Here is the Coq definition of natural numbers:

  Inductive nat : Set  :=   O : nat  |  S : nat → nat.

Another, quite independent change in the treatment of inductive definitions was the split of the elimination constant into two constructions Cases and Fix implementing respectively case analysis and the fixpoint operator, making the writing of primitive recursive functions much more intuitive. For example the function plus can now be given in the MLstyle as:

  Fix plus/1 : nat→nat→nat :=
     [n:nat][m:nat]Cases n of
                     O ⇒ m
                   | (S n') ⇒ (S (plus n' m))
                   end

making the recursion explicit. Of course not every expression is allowed as a body of Fix, as this would lead to non-terminating computations. The user must point out a recursion argument that structurally decreases in every recursive call. In the case of plus, the recursion is made on the first argument (which is pointed out with /1) and indeed, as n is matched against (S n'), n' is structurally smaller in the recursive call (plus n' m).

Primitive inductive types together with the possibility of defining types (and propositions) by case analysis on inductive types (called strong elimination) overcome the main disadvantages of the impredicative encoding. It is now possible to prove that 0 ≠ 1, induction schemes can be proved using elimination, and writing simple primitive recursive functions follows the ML-style.

1.4 Rewriting

Equality is ubiquitous in mathematics. Yet it turns out that proof assistants based on the Curry-Howard isomorphism are not very good at handling equality. While proving an equality is not a problem in itself, using already established equalities is quite problematic. Apart from equalities resulting from internal reductions (beta, iota), which can be used without being recorded in the proof term by using the Conversion rule, any other use of an equality requires giving all details of its use explicitly in the proof term: the name (proof) of the equality, the place where the equality is used in the form of an abstracted context, and both the left-hand and right-hand sides of the equality. A single equality step may generate a subexpression of the proof term, whose size is proportional to the size of the goal. As a result, proof terms may become extremely large, requiring sometimes more storage than available and making type-checking time consuming: working with equations in Coq is not very convenient.

A straightforward idea for reducing the size of proof terms is to allow using other equalities in the conversion rule, making their use transparent. As usual, this is done in practice by using the user-defined equalities as one-way rewrite rules. In order to keep a decidable conversion rule, these rewrite rules must of course be checked for strong normalization and confluence.

Existing techniques for proving strong normalization of rewriting in the calculus of construction [3, 13, 11, 12, 98, 99], following numerous works about rewriting in the simplytyped lambda calculus [53, 54, 55, 14], assume that the rewriting system is given as a whole from the beginning. It is of course very undesirable to impose such a condition on the users of an interactive proof assistant. Fortunately, these termination criteria are modular, meaning that successive rewriting systems may in fact be added one by one. Introducing definitions by rewriting becomes then very similar to giving named inductive types. It requires an environment E^{Γ} = v_{1} : t_{1} … v_{n} : t_{n}, containing the names and types of the symbols defined by the rewrite rules, and a set of rules R = {E_{i} | e_{i}^{L} ⟶ e_{i}^{R} : t̂_{i}}_{i=1...m}, where E_{i} = v_{1}^{i} : t_{1}^{i} … v_{n_{i}}^{i} : t_{n_{i}}^{i} is a rule environment, e_{i}^{L} and e_{i}^{R} are the left-hand and right-hand sides of the rule and t̂_{i} the type common to both sides:

\underline{Formation}

\frac{E ⊢ ok E ⊢ t_{k} : s_{k} for k = 1 … n \begin{matrix}E; E_{i} ⊢ ok E; E_{i} ⊢ t̂_{i} : ŝ_{i} \\ E; E^{Γ}; E_{i} ⊢ e_{i}^{L} : t̂_{i} E; E^{Γ}; E_{i} ⊢ e_{i}^{R} : t̂_{i} \end{matrix} \} for i = 1 … m}{E; RewDef(E^{Γ}, R) ⊢ ok} if ACC_{E}(E^{Γ}, R)

where ACC_{E}(E^{Γ}, R) is an acceptance condition which plays a role similar to the positivity condition for inductive types, by syntactically ensuring strong normalization and confluence of rewriting. Research for coming up with flexible and easy to implement syntactic conditions is still quite active [99, 12]. We will not describe these results here, but rely on the assumption that reasonable criteria exist for that purpose.

Once a definition by rewriting is in the environment, we can use it either to extract the type of a defined function or to perform computations.

\underline{Function}  \frac{E′; RewDef(E^{Γ}, R); E′′ ⊢ ok}{E′; RewDef(E^{Γ}, R); E′′ ⊢ v_{j} : t_{j}}

\underline{Computation}  \frac{E ⊢ ok δ : E_{i} → E}{E ⊢ e_{i}^{L}δ ⟶_{R} e_{i}^{R}δ}

where E = E′; RewDef(E^{Γ}, R); E′′ for some E′ and E′′. The notation δ : E_{i} → E means that E ⊢ vδ : tδ for every v : t ∈ E_{i}.

The new reduction relation ⟶_{R} becomes now part of the conversion relation ≈. Using rewriting, it becomes possible to define addition for natural numbers as follows:

  Symbol plus : nat → nat → nat
  Rules
    plus O y ⇒ y
    plus (S x) y ⇒ S (plus x y)
    plus x O ⇒ x
    plus x (S y) ⇒ S (plus x y)
    plus x (plus y z) ⇒ plus (plus x y) z

In the above definition the first two rules correspond to the definition by Cases and Fix presented in the previous section, and the other three represent equalities which are only provable by induction. Therefore using the definition of plus based on rewriting becomes much easier than using the one with Fix and Cases, since all rewrite rules can now be applied automatically in the conversion.

The above syntax is inspired by the experimental “Rewriting” branch of Coq, developed by Fréderic Blanqui. This new facility is not yet provided in the distributed version of Coq however.

1.5 Modules

In order to certify programs, it is important that a proof assistant provides users with facilities to manage large developments. Coq supports multi-file developments and the structure of a file can be nicely presented using sections. Moreover, sections provide a mechanism for abstracting individual definitions and theorems. However, all versions of Coq prior to version 7.4 are lacking the possibility to conveniently define parameterized theories or parametrized certified data structures, which are needed to make development in the large possible in practice.

In functional programming languages from the ML family, such facilities are provided by the module system [65]. Modules group together related definitions, and higher-order modules (functors) make massive abstraction very convenient.

Modules make a small typed language of their own, where module types play a role of interfaces for modules. On one hand they are used to distinguish the definitions of general interest from auxiliary definitions, on the other hand they specify parameters of higher-order modules.

1.5.1 Informal presentation

In this section, we briefly describe the main elements of an ML-style module system, using very simple examples in a Coq-like syntax. We start with basic module expressions, called structures.

Structures. A structure corresponds to a list of definitions and lemmas (together with their proofs) enclosed in the keywords Struct and End. For example, a structure containing a definition of a type T and an element x of that type is declared as follows:

  Struct
    Definition T := nat.
    Definition x := O.
  End.

In the sequel we refer to this module expression as m.

Signatures. A type of a structure is a signature. A possible type for the structure m given above could be:

  Sig
    Definition T := nat.
    Parameter x : T.
  End.

Note that this signature hides the value of x, while showing the value of T.

Functors. A functor is a module abstraction. The following functor (further referred to as F) takes as argument a module X whose signature contains a type declaration and an element of that type, and returns a function type together with a constant function.

  Functor [ X : Sig
                  Parameter T : Set.
                  Parameter x : T.
                End ]
        Struct
          Definition T := X.T → X.T.
          Definition x := [a:X.T] X.x.
        End

The notation X.T used above denotes the selection of the T component of the module X. This allows to distinguish between the type T of the argument module X and the type T returned by the functor F.

Functor types. Functors must of course themselves have types. The above functor may be assigned the following functor type:

  Funsig ( X : SIG ) SIG

where SIG is the signature

   Sig
     Parameter T : Set.
     Parameter x : T.
   End

Higher-order functors. The following functor, further referred to as H, is a higherorder functor, applying its first argument, which is itself a functor, to its second argument:

  Functor [ G : Funsig ( X : SIG ) SIG ]
    Functor [ Y : SIG ]
      (G Y)

A higher order module type of this functor could be

  Funsig ( G : Funsig ( X : SIG ) SIG )
    Funsig ( Y : SIG )
      SIG

Subtyping. It is now possible to apply the higher order functor H to the functor F and the module m, forming a correct module expression (H F m). This application is made possible by the module subtyping mechanism, which allows the module expression m to be typed with the signature SIG and consequently to the module expression (H F) of type Funsig(Y:SIG)SIG to be applied to m. The subtyping relation is written <:

At the signature level, the module subtyping relation allows a signature defining more elements or in a more precise manner to be a subtype of a signature with less elements or a less precise description. For example:

             Sig
               Definition T:Set:=nat.
               Parameter x:T.
             End

<:

                                                   Sig
                                                     Parameter T:Set.
                                                   End

because there are more elements on the left than on the right, and the element T is more precisely described on the left — as a type equal to nat — than on the right. From the second of the above reasons it follows that the left-hand side signature is also a subtype of the signature SIG.

At the functor type level, the subtyping relation is a bit more complex, since it is contravariant in the argument type and co-variant in the result type.

In general, one can say that a module system is quite similar to a dependently typed lambda calculus with nested records, record types and monotone record type subtyping. Structures correspond to dependent records, signature to record types, functors to dependent functions and functor types to dependent products.

1.5.2 Module system for type theory

The first module system designed for a proof assistant based on the Curry-Howard isomorphism is due to Judicaël Courant [31, 32, 33]. In this system, modules are anonymous second-class objects: module expressions cannot be abstracted with respect to terms and vice versa. The associated reduction rules enjoy the necessary metatheoretical properties: subject reduction, strong normalization and confluence. As a result, the system obtained by extending an arbitrary pure type system (without inductive types or rewriting) by Courant’s module system is conservative. The module system described in this thesis and implemented in Coq is based on Courant’s, and we will therefore present this system in detail.

Specifications. In the following we will use the small letter m to denote module expressions (structures and functors) and the capital letter M to denote module types (signatures and functor types).

We already noticed in Section 1.2 that entries in the environment of a proof assistant can have the form of a definition v : t := e or of a variable declaration v : t. Given that the same two options are possible for modules and that signatures and structures contain entries similar to those in the environment, the type system would contain many rules having a very similar appearance. In order to limit the complexity of the type system, Courant decided to introduce specifications, which are syntactic objects attached to variables in an environment (and signatures and structures) giving whatever the information is available about these variables. Specifications are also used to formalize signature subtyping in a succinct way.

In Courant’s calculus the environment is a list of pairs made of a variable and its specification. The variable declaration v : t from Section 1.2 becomes v : Ty(t) where Ty(t) is an abstract specification, whose meaning is that the object named v is of type t. The definition v : t := e becomes v : Eq(e : t) where Eq(e : t) is a manifest specification, whose meaning is that the object named v is equal to e of type t.^{7}

The rules for building correct environments are now split into abstract formation rules assuming correct specifications, and concrete rules for building correct abstract or manifest specifications. We start with the rules for building environments, E being as usual our favorite letter for denoting an environment:

\underline{Empty}  \overline{ϵ ⊢ ok}
\underline{Insert}  \frac{E ⊢ ok E ⊢ S : spec}{E; v : S ⊢ ok}  \underline{Lookup}  \frac{E_{1}; v : S; E_{2} ⊢ ok}{E_{1}; v : S; E_{2} ⊢ v : S}

In the rules above, S is any specification and spec is a new sort used in judgments E ⊢ S : spec denoting that S is a correct specification in the environment E.^{8}

In the following rule schemes for building correct specifications, we use Greek letters ϕ, Ψ and Ξ. They stand for terms e, types t and PTS sorts s respectively, if we are interested in term specifications, and for modules m, module types M and a sort modtype if we are interested in module specifications. The new sort modtype is used in judgments of the form E ⊢ M : modtype, whose meaning is that M is a correct module type. The rules are split into two sets, for abstract specifications first:

\underline{Formation}  \frac{E ⊢ Ψ : Ξ}{E ⊢ Ty(Ψ) : spec}

\underline{Elimination}  \frac{E ⊢ ϕ : Ty(Ψ)}{E ⊢ ϕ : Ψ}

Satisfaction

\frac{E ⊢ ϕ : Ψ E ⊢ Ty(Ψ) : spec}{E ⊢ ϕ : Ty(Ψ)}

and then for manifest specifications:

\underline{Formation}  \frac{E ⊢ ϕ : Ψ}{E ⊢ Eq(ϕ : Ψ) : spec}

7For subjective esthetic reasons, we changed a bit the notation adopted by Courant. In the original, the specification Eq(e : t) was written Ty(t)Eq(e).

8Our spec replaces wfs from [32], which stands for well-formed specification.

\underline{Elimination}  \frac{E ⊢ ϕ′ : Eq(ϕ : Ψ)}{E ⊢ ϕ′ : Ψ}

Satisfaction \frac{E ⊢ ϕ′ ≈ ϕ E ⊢ ϕ′ : Ψ E ⊢ Eq(ϕ : Ψ) : spec}{E ⊢ ϕ′ : Eq(ϕ : Ψ)}

\underline{Computation}  \frac{E ⊢ ϕ′ : Eq(ϕ : Ψ)}{E ⊢ ϕ′ ≈ ϕ}

Note that an abstract/manifest specification Formation followed by an Insert corresponds to the Insert rules for variables/constants presented in Section 1.2. Similarly, a Lookup followed by an Elimination corresponds to the Lookup rule from Section 1.2. And a Lookup followed by the Computation rule for manifest specifications allows to derive the equality corresponding to the δ-reduction from Section 1.2. Finally, the Satisfaction rules are necessary for forming structures and for signature subtyping as we will see in the next paragraphs.

Structures and signatures. Structures, as they have been introduced in the informal presentation, contain definitions and lemmas with their proofs, and possibly sub-modules. In other words they correspond to closed developments. Note that definitions and lemmas are quite different in essence. In the case of definitions, the equality between the name of the constant and its body is important and may be used for typing terms; in the case of lemmas, the only important thing is that the lemma has a proof: the equality between the name of the lemma and its proof is never going to be used for typing.

Therefore, a component of a structure consists of a name, its specification and its implementation. The specification provides the information available for typing subsequent components of the structure, and the implementation provides a witness or a proof of validity of the specification. A lemma v stating the property t with its proof e will be a structure component of the form v : Ty(t) := e, and a definition v′ of type t′ and body e′ will be a structure component of the form v′ : Eq(e′ : t′) := e′. Although it may seem strange that the body e′ is both a part of the specification and the witness, this duplication makes typing rules for structures simple and elegant.

A signature, on the other hand, contains the information relevant for typing only, i.e. the names of its components and their specifications.

Before giving the formal syntax for structures and signatures, we have to note that the concrete syntax given in the examples at the beginning of the present section is not sufficient to express certain nested structures and signatures. For example, in the informal signature

       Sig
         Parameter T:Set.
         Parameter x:T.
         Module N : Sig
                      Parameter T:Set.
                      Definition y:=x.
                      Parameter z:T.

                    End.
       End

further referred to as SIG', it is impossible to assign a type to the component y of the internal module. Its type should be the T defined in the main signature, but there is no way to refer to it after the identifier T has been redefined in the submodule. A simple solution would be to rename either of the two T components, but this is not always possible, because of the possible interaction with functors. Following [48] and [32], our formal syntax will therefore make a difference between labels visible from the exterior and α-convertible binders used inside a given structure or signature, as follows:

Struct v_{1}▷v_{1} : S_{1} := ϕ_{1} … v_{n}▷v_{n} : S_{n} := ϕ_{n} End
Sig v_{1}▷v_{1} : S_{1} … v_{n}▷v_{n} : S_{n} End

where v denotes an external label, v an internal binder, S a specification and ϕ an implementation, which can be a term e or a module expression m. From this point on we assume that unless stated otherwise, all variables v bound in environments, structures, signatures, lambdas and functors are different. In the syntax with labels and binders, the signature SIG' can be written as follows:

Sig T ▷ T : Ty(Set) x ▷ x : Ty(T) N ▷ N : Ty( Sig T ▷ T′ : Ty(Set) y ▷ y : Eq(x : T) z ▷ z : Ty(T′) End )

End

making the name clash disappear. Because it is a bit heavy, we do not intend to follow this formal syntax in the examples: we will actually abbreviate b ▷ b as b.

A signature M is correct, which is asserted by the judgment E ⊢ M : modtype, if its labels are disjoint and the component specifications are correct. This yields the following typing rule for signature formation:^{9}

\underline{Formation}  \frac{E; v_{1} : S_{1} … v_{n} : S_{n} ⊢ ok}{E ⊢ Sig v_{1}▷v_{1} : S_{1} … v_{n}▷v_{n} : S_{n} End : modtype}

if v_{1} … v_{n} are pairwise disjoint labels

A structure is correct if so are its component specifications, and if each implementation ϕ_{k} satisfies its specification S_{k}, possibly using the information contained in the specifications S_{1} … S_{k−1}. The yields the following typing rule for structure formation:

9In [32] there are two rules: for forming an empty signature and for adding a specification to a signature. We decided to give the more intuitive single-rule presentation with dots. Both approaches are of course equivalent. The same difference in presentation concerns the Structure rule and the definition of δ-reduction.

\underline{Structure}

E ⊢ Sig v_{1}▷v_{1} : S_{1} … v_{n}▷v_{n} : S_{n} End : modtype
E; v_{1} : S_{1} … v_{k−1} : S_{k−1} ⊢ ϕ_{k} : S_{k} for k = 1 … n

E ⊢ Struct v_{1}▷v_{1} : S_{1} := ϕ_{1} … v_{n}▷v_{n} : S_{n} := ϕ_{n} End : Sig v_{1}▷v_{1} : S_{1} … v_{n}▷v_{n} : S_{n} End Once a module expression m has been proved to satisfy a given signature, we may extract its components:

\underline{Access} (E ⊢ m : Sig v_{1}▷v_{1} : S_{1} … v_{n}▷v_{n} : S_{n} End) / (E ⊢ m.v_{k} : S_{k}{v_{i} ↦ m.v_{i}}_{i=1...k−1})

The substitution in the conclusion is needed since S_{k} may contain free occurrences of the variables v_{1} … v_{k−1}. For example, given a module K of signature SIG', defined as above, it is possible to derive the specification of K.x to be Ty(K.T).

Functors. Like lambda abstractions are functional terms, functors are functional modules, and therefore functor types correspond to dependent products. Rules for forming functor types and for typing functors and their applications are very similar to the rules for products, term abstraction and application:

\underline{Formation}  \frac{E ⊢ M_{1} : modtype E; v : Ty(M_{1}) ⊢ M_{2} : modtype}{E ⊢ Funsig(v:M_{1}) M_{2} : modtype}

\underline{Functor}  \frac{E; v : Ty(M_{1}) ⊢ m : M_{2} E ⊢ Funsig(v:M_{1})M_{2} : modtype}{E ⊢ Functor[v:M_{1}] m : Funsig(v:M_{1})M_{2}}
\underline{Application}  \frac{E ⊢ m : Funsig(v : M_{1}) M_{2} E ⊢ m′ : M_{1}}{E ⊢ m m′ : M_{2}\{v ↦ m′\}}

Subtyping. Before defining the subtyping for signatures and functor types, let us see its use via the following subsumption rule. It says that a module expression of type M_{1} is of any module type M_{2} that is a supertype of M_{1}:

\underline{Subsumption}  \frac{E ⊢ m : M_{1} E ⊢ M_{1} <: M_{2}}{E ⊢ m : M_{2}}

For signatures, subtyping corresponds to field extension:

\underline{Signatures}

E ⊢ Sig v_{1}▷v_{1} : S_{1} … v_{n}▷v_{n} : S_{n} End : modtype
E ⊢ Sig v′_{1}▷v′_{1} : S′_{1} … v′_{n′}▷v′_{n′} : S′_{n′} End : modtype
{v′_{1} … v′_{n′}} ⊆ {v_{1} … v_{n}}
by α-conversion, we assume v′_{i′} = v_{i} implies v′_{i′} = v_{i}
E; v_{1} : S_{1} … v_{n} : S_{n} ⊢ v′_{k} : S′_{k} for k = 1 … n′
E ⊢ Sig v_{1}▷v_{1} : S_{1} … v_{n}▷v_{n} : S_{n} End <: Sig v′_{1}▷v′_{1} : S′_{1} … v′_{n′}▷v′_{n′} : S′_{n′} End

A more precise but less readable version of this rule uses an explicit injection on indices describing the inclusion between the sets of labels {v′_{1} … v′_{n′}} and {v_{1} … v_{n}} instead of this informal reference to α-conversion.^{10}

Note that the premises E; v_{1} : S_{1} … v_{n} : S_{n} ⊢ v′_{k} : S′_{k} can be derived using the specification satisfaction rule allowing in particular to derive v_{k} : Ty(t) when the specification of v_{k} in the environment is Eq(e : t). For module components the Subsumbption rule may also be used.

For functor types, subtyping is contravariant in the argument type and covariant in the result type:

Functor types \frac{E ⊢ M′_{1} <: M_{1} E; v : Ty(M′_{1}) ⊢ M_{2} <: M′_{2}}{E ⊢ Funsig(v:M_{1}) M_{2} <: Funsig(v:M′_{1}) M′_{2}}

This rule can be understood as follows: given a functor transforming a witness of the module type M_{1} into a witness of the module type M_{2}, the same functor can be seen as transforming a witness of a richer module type M′_{1} into a witness of a poorer module type M′_{2}.

Strengthening. Although the module system given so far looks satisfactory, it will reject the following natural definitions:

X : Ty( Sig T : Ty(Set) End ) := Struct T : Ty(Set) := nat End
Y : Ty( Sig T : Eq(X.T : Set) End ) := X

Indeed, the typing system given so far does not allow to derive the following obvious statement:

X : Sig T : Eq(X.T : Set) End

To remedy this situation, we need the following strengthening rule:

Strengthening

\frac{E ⊢ m : M}{E ⊢ m : M/m}

where the notations M/m and S/ϕ are defined together as follows:

Sig v_{1}▷v_{1} : S_{1} … v_{n}▷v_{n} : S_{n} End/m = Sig v_{1}▷v_{1} : S_{1}/m.v_{1}; …; v_{n}▷v_{n} : S_{n}/m.v_{n} End
(Funsig(v:M_{1}) M_{2})/m = Funsig(v:M_{1}) (M_{2}/(m v))
Eq(ϕ′ : Ψ)/ϕ = Eq(ϕ′ : Ψ/ϕ)
Ty(Ψ)/ϕ = Eq(ϕ : Ψ/ϕ)
t/e = t

Using Strengthening, one can now show the desired module typing above, since

Sig T : Ty(Set) End/X = Sig T : Ty(Set)/X.T End = Sig T : Eq(X.T : Set) End

It can be shown that we can derive E ⊢ M/m <: M whenever E ⊢ m : M is itself derivable.

10The presentation with an explicit injection can be found in [32].

Convertibility. We now come to the reduction rules needed for these anonymous modules. First of all, β-reduction applies to module expressions:^{11}

Functor[v : M]m m′ ⟶_{β_{m}} m{v ↦ m′}

The convertibility relation ≈ is now defined to be the smallest equivalence relation closed by context, containing β-equality on terms and β_{m}-equality on modules, as well as the equality following from the Computation rule for Eq(·) specifications.

In fact, the latter part of the convertibility relation may also be defined as a reduction. The reduction is of course called δ, but due to the complexity of the module calculus, its definition requires 7 rules^{12} deriving judgments of the form E ⊢_{δ} m : M and E ⊢_{δ} ϕ : S. We give the complete definition below, which will be used in Section 6.3.

Definition 1.5.1. Let delta reduction ⟶_{δ} be defined as the smallest congruence such that E ⊢ ϕ ⟶_{δ} ϕ′ if E ⊢_{δ} ϕ : Eq(ϕ′ : Ψ) is derivable in the following inference system:

[/S/ACCESS]

E ⊢_{δ} m : Sig v_{1}▷v_{1} : S_{1} … v_{n}▷v_{n} : S_{n} End
E ⊢_{δ} m.v_{i} : S_{i}{v_{j} ↦ m.v_{j}}_{j=1..i−1}

[/S/VAR]

\overline{E; v : S; E′ ⊢_{δ} v : S}

[/S/ABS]

\frac{E ⊢_{δ} m : M}{E ⊢_{δ} m : Ty(M)} if m is not a variable v or a selection m′.v

[/T/SPEC]

\frac{E ⊢_{δ} m : S S = Ty(M) or Eq(m′ : M)}{E ⊢_{δ} m : M} if m is a variable v or a selection m′.v

[M/T/APP]

\frac{E ⊢_{δ} m_{1} : Funsig(v : M)M′}{E ⊢_{δ} (m_{1} m_{2}) : M′\{v ↦ m_{2}\}}

[M/T/LAM]

\frac{E; v : Ty(M) ⊢_{δ} m : M′}{E ⊢_{δ} Functor[v : M]m : Funsig(v : M)M′}

[M/T/STRUCT]

E ⊢_{δ} Struct v_{1}▷v_{1} : S_{1} := ϕ_{1} … v_{n}▷v_{n} : S_{n} := ϕ_{n} End : Sig v_{1}▷v_{1} : S_{1} … v_{n}▷v_{n} : S_{n} End These rules allow to δ-reduce the term (F m).v to m.t in the environment containing the declaration

11In [32] it is called μ, but in the later paper [33] its name is changed to β_{m}.

12The rule [M/T/STRUCT] replaces two rules [M/T/EMPTY] and [M/T/DECL] in [32]. See footnote 9.

F : Ty( Funsig(X : M)Sig v : Eq(X.t : Set) End )

On the other hand, it is not possible to derive the statement E ⊢ v ⟶_{δ} v, even though obviously E ⊢ v : Eq(v : Ψ) is derivable for any v in E, using the Satisfaction rule.

Courant shows that δ and β_{m} reductions together have the subject reduction property, are strongly normalizing, and are confluent modulo β on well-typed expressions. Given an arbitrary normalizing PTS, these properties yield an algorithm for deciding convertibility for the associated module calculus.

Conservativity. The conservativity of the module calculus over the base PTS is shown using yet another reduction relation on modules, called ρ-reduction:

m.v_{i} ⟶_{ρ} ϕ_{i}{v_{j} ↦ m.v_{j}}_{j=1...i−1}

where m = Struct v_{1}▷v_{1} : S_{1} := ϕ_{1} … v_{n}▷v_{n} : S_{n} := ϕ_{n} End. This reduction is indeed necessary for the proof of conservativity, because otherwise there are irreducible terms of basic type containing module expressions, such as

Struct v : Ty(t) := e End.v

Courant shows that this reduction relation also has the subject reduction property, and that ρβ_{m} reductions are strongly normalizing. Consequently, given a term of base type, its module subterms may be reduced until they eventually disappear. Therefore, any base type inhabited in the calculus with modules is inhabited in the calculus without modules.

1.6 Anonymous modules, inductive types and rewriting

The calculus of anonymous modules is very appealing from the theoretical point of view: it naturally extends the λ-calculus approach in which defining an entity (e.g., a function) does not necessary mean naming it. Besides, modules expressions are true citizens of type theory rather than a bare practical feature of proof assistants.

On the other hand, the fact that modules are anonymous is hardly useful in practice. Modules are by nature large and complex syntactic objects so terms containing them become quite unreadable. Fortunately, the module specifications make it possible to use modules in such a way, that only simple module expressions appear in terms, a property which is stable by reduction: if every module definition v : S := m is given with an abstract specification, i.e. S = Ty(M), then no reduction can substitute the body m in a term referring to the module name v.

Consequently, one can implement a module system with some simple syntactic restrictions, like imposing abstract specifications on module components and ruling out complex module expressions from terms, and still benefit from the conservativity proof of Courant.

On the other hand, before implementing a module system for Coq one has to make sure it is compatible not only with the underlying pure type system, but also with inductive types and rewriting which is an important future extension of Coq. In his thesis Courant argues that extending his calculus with anonymous inductive types should be quite straightforward. Inspired by the typing rules for inductive types one could also define anonymous rewriting in a similar way and still expect that the conservativity result on modules would hold.

Anonymous rewrite rules have already been studied in the literature under the name of Rho Calculus [21]. In this framework, a rewrite rule is a binder (like λ in the λ-calculus) which is “used up” when applied. Although this formalism unifies very cleanly the λcalculus with rewriting, it is not suitable for our purpose. It is for example impossible in the typed version of the Rho Calculus [22] to define a function plus such that for all natural numbers n, m the application plus S^{n}(O) S^{m}(O) reduces to S^{n+m}(O) (where S^{i}(O) means of course S applied i times to O)^{13}. Since in our calculus, rewriting aims at subsuming definitions by fixpoint (the Fix operator from Coq) we adopted a different approach, in which function symbols have associated rewrite rules that can be applied as many times as necessary.

In this section, we investigate addition of anonymous inductive definitions and anonymous definitions by rewriting to the calculus of modules. We will see that existing specifications are not sufficient to make the extended system usable and we will propose new kinds of specifications to remedy this situation. We will also see that the closure properties imposed by module reductions on acceptance conditions for rewrite rules are far too strong for termination criteria existing in the literature.

Anonymous rewriting. Let us recall the definition of addition on natural numbers given in Section 1.4. Let E^{Γ} denote the environment plus : Ty(nat → nat) and R be the set of rewrite rules:

    plus O y ⇒ y
    plus (S x) y ⇒ S (plus x y)
    plus x O ⇒ x
    plus x (S y) ⇒ S (plus x y)
    plus x (plus y z) ⇒ plus (plus x y) z

The definition package, Rew(E^{Γ}, R), could than be used in a way similar to inductive packages, i.e. Rew(E^{Γ}, R).1 would be a term denoting the function plus. This notation Rew(E^{Γ}, R).i is indeed necessary for handling mutually defined functions.

Rules defined inside a package would yield reductions valid outside the package. For example, the first rule could be used to make the reduction

Rew(E^{Γ}, R).1 O (S (S O)) ⟶_{R} (S (S O))

13Such a definition is possible in the untyped Rho Calculus, but it has to contain a non-terminating term, similar to the fixpoint combinator Y of the untyped λ-calculus.

while the last rule could be used to make the more complex reduction

Rew(E^{Γ}, R).1 x (Rew(E^{Γ}, R).1 y z) ⟶_{R} Rew(E^{Γ}, R).1 (Rew(E^{Γ}, R).1 x y) z

Old specifications. Unfortunately, unlike modules, anonymous inductive definitions and anonymous definitions by rewriting cannot be used conveniently through specifications. Taking again our favorite example of natural numbers, a module defining them could either be given a completely abstract signature:

N′ : Ty( Sig nat : Ty(Set); O : Ty(nat); S : Ty(nat → nat) End )

or a completely transparent signature:

N′′ : Ty( Sig nat : Eq(Ind(E^{I} := E^{C}).1 : Set); O : Eq(Ind(E^{I} := E^{C}).2 : nat); S : Eq(Ind(E^{I} := E^{C}).3 : nat → nat) End )

In the first case the information about the inductive character of natural numbers is completely hidden and there is no way one can have any syntactic constructions for inductive types like Cases and Fix. In the second case N′′.nat, N′′.0 and N′′.S simply reduce to their anonymous bodies as described in Section 1.3, yielding terms whose reading is difficult.

The same happens with rewriting. A module containing the definition of plus can either be given an abstract signature

P′ : Ty( Sig plus : Ty(nat → nat) End )

or a completely transparent one

P′′ : Ty( Sig plus : Eq(Rew(E^{Γ}, R).1 : nat → nat) End )

In the first case P′.plus cannot be used to calculate and in the second case P′′.plus can be unfolded to the unreadable expression Rew(E^{Γ}, R).1.

New specifications. To remedy this unsatisfactory situation, we need to introduce two new kinds of specifications, IndSpec(E^{I} := E^{C}) and RewSpec(E^{Γ}, R), with the respective intended meanings “these elements form an inductive definition” and “these elements have rewrite rules R attached to them”. Using these new specifications, we could give the following signatures to modules defining natural numbers and addition:

N′′′ : Ty( Sig nat, O, S : IndSpec(E^{I} := E^{C}) End )
P′′′ : Ty( Sig plus : RewSpec(E^{Γ}, R) End )

Consequently, the expressions N′′′.nat, N′′′.0 and N′′′.S are irreducible, but the fact that they form an inductive definition follows from the form of their specification. Similarly P′′′.plus itself is irreducible, but the reduction rules for plus are available for P′′′.plus. For example, using the second rewrite rule from R, the following reduction applies:

P′′′.plus (S O) O ⟶_{R} S (P′′′.plus O O)

Before giving typing rules for IndSpec(E^{I} := E^{C}) and RewSpec(E^{Γ}, R), let us discuss how these specifications should behave with respect to strengthening.

The result of strengthening Sig plus : RewSpec(E^{Γ}, R) End by P′′′ should both indicate that plus is equal to P′′′.plus and that plus is defined by rewriting. The solution consists in wrapping RewSpec(E^{Γ}, R) in either an abstract or a manifest specification. So, in the true formal syntax our modules N′′′ and P′′′ should be given the following specifications:

N′′′ : Ty( Sig nat, O, S ▷ nat, O, S : Ty(IndSpec(E^{I} := E^{C})) End )
P′′′ : Ty( Sig plus ▷ plus : Ty(RewSpec(E^{Γ}, R)) End )

and the strengthened signatures of N′′′ and P′′′ would be respectively

Sig nat, O, S ▷ nat, O, S : Eq(P′′′.nat, P′′′.O, P′′′.S : IndSpec(E^{I} := E^{C})) End
Sig plus ▷ plus : Eq(P′′′.plus : RewSpec(E^{Γ}, R)) End

1.6.1 Blocks and their typing

We have seen that term specifications have insufficient expressivity to conveniently use inductive definitions and definitions by rewriting in the calculus of modules. It turns out that one needs a means to specify a whole sequence of terms at the same time.

Besides, expressions like Ind(E^{I} := E^{C}) and Rew(E^{Γ}, R) represent a sequence of mutually defined terms. Therefore, in order to make the typing system more compact, we decided to create a new syntactic class of blocks, representing sequences of terms, possibly mutually defined. A block can be either a basic block, i.e. an inductive definition Ind(E^{I} := E^{C}) or a definition by rewriting Rew(E^{Γ}, R), or a normal sequence of terms.

For blocks, we introduce the same hierarchy that we have for terms and modules. We have terms, types, sorts (which happen to be the same syntactic class), and term specifications Ty(t) and Eq(e : t). We have module expressions, module types, the sort modtype, and module specifications Ty(M) and Eq(m : M). For blocks (denoted by B), we introduce block types IndSpec() and RewSpec() (denoted by BT), a new sort blocktype and block specifications Ty(BT) and Eq(B : BT).

As we have already mentioned, we can build a block from individual terms: a block being a sequence of terms will be denoted by \overline{e}. We can also extract individual terms from a block B, using the selection operation, denoted by B.i. If B is a basic block, we get terms Ind(E^{I} := E^{C}).i or Rew(E^{Γ}, R).i and if B is a sequence of terms, \overline{e}.i is the i^{th} term of the sequence.

In order to present the calculus with these new specifications, we modify slightly the syntax of environments, signatures and structures. An environment is now a sequence of pairs: a sequence of variables \overline{v} and a specification S. For term and module environment entries the sequences \overline{v} will be of length 1. Similarly, signatures assign a specification to a sequence of labels \overline{v} and a sequence of internal binders \overline{v} (of equal length). Structures, apart from specifications, assign also an implementation, which is now a term, a module expression, or a block.

We are now going to present the typing rules for blocks. For inductive blocks they are very similar to those for inductive definitions presented in Section 1.3. Let E^{I} = v_{1}^{I} : Ty(t_{1}) … v_{k}^{I} : Ty(t_{k}) and E^{C} = v_{1}^{c} : Ty(t̂_{1}) … v_{n}^{c} : Ty(t̂_{n})

\underline{Formation} \frac{E ⊢ t_{j} : s_{j} for j = 1 … k E; E^{I} ⊢ t̂_{i} : s′_{i} for i = 1 … n}{E ⊢ IndSpec(E^{I} := E^{C}) : blocktype} if POS_{E}(E^{I} := E^{C})

\underline{Definition}  \frac{E ⊢ IndSpec(E^{I} := E^{C}) : blocktype}{E ⊢ Ind(E^{I} := E^{C}) : IndSpec(E^{I} := E^{C})}

\underline{Inductive Type} \frac{E ⊢ B : IndSpec(E^{I} := E^{C})}{E ⊢ B.j : t_{j}} for j = 1 … k

\underline{Constructor} \frac{E ⊢ B : IndSpec(E^{I} := E^{C})}{E ⊢ B.i : t̂_{i−k}\{v_{j}^{I} ↦ B.j\}_{j=1...k}} for i = k + 1 … n

The main role of an anonymous basic block Ind(E^{I} := E^{C}) is to provide an implementation for the inductive specification in a structure. It can of course also appear in terms as an anonymous inductive definition, using the the rules Inductive Type and Constructor.

The typing rules for rewrite blocks are similar to those given in Section 1.4 for definitions by rewriting. Let E^{Γ} = v_{1} : Ty(t_{1}) … v_{n} : Ty(t_{n}) and R = {E_{i} | e_{i}^{L} ⟶ e_{i}^{R} : t̂_{i}}_{i=1...m}, where E_{i} = v_{1}^{i} : Ty(t_{1}^{i}); …; v_{n_{i}}^{i} : Ty(t_{n_{i}}^{i})

\underline{Formation}

\frac{E ⊢ t_{k} : s_{k} for k = 1 … n \begin{matrix}E; v_{1}^{i} : Ty(t_{1}^{i}); …; v_{j−1}^{i} : Ty(t_{j−1}^{i}) ⊢ t_{j}^{i} : s_{j}^{i} for j = 1 … n_{i} \\ E; E^{Γ}; E_{i} ⊢ t̂_{i} : ŝ_{i} E; E^{Γ}; E_{i} ⊢ e_{i}^{L} : t̂_{i} E; E^{Γ}; E_{i} ⊢ e_{i}^{R} : t̂_{i} \end{matrix} \} for i = 1 … m}{E ⊢ RewSpec(E^{Γ}, R) : blocktype}

if ACC_{E}(E^{Γ}, R)

\underline{Definition} \frac{E ⊢ RewSpec(E^{Γ}, R) : blocktype}{E ⊢ Rew(E^{Γ}, R) : RewSpec(E^{Γ}, R)} if COMP_{E}(E^{Γ}, R)

The side condition COMP_{E}(E^{Γ}, R), is a completeness condition. It is supposed to assure that all functions from E^{Γ} are totally defined by R. The meaning of this condition is explained in the next subsection (paragraph “Rewriting in structures”) and discussed in Chapter 11.

\underline{Function}  \frac{E ⊢ B : RewSpec(E^{Γ}, R)}{E ⊢ B.j : t_{j}}

\underline{Computation} \frac{E ⊢ B : RewSpec(E^{Γ}, R) δ : E_{i} → E}{E ⊢ e_{i}^{L}θδ ≈ e_{i}^{R}θδ} θ = \{v_{j} ↦ B.j\}_{j=1...n}

The notation δ : E_{i} → E means that E ⊢ vδ : tδ holds for every v : Ty(t) ∈ E_{i}.

\underline{Satisfaction} \frac{E ⊢ RewSpec(E^{Γ}, R) : blocktype E ⊢ B.j : t_{j} for j = 1 … n E; E_{i} ⊢ e_{i}^{L}θ ≈ e_{i}^{R}θ for i = 1 … m}{E ⊢ B : RewSpec(E^{Γ}, R)} θ = \{v_{j} ↦ B.j\}_{j=1...n}

The Satisfaction rule is mainly useful for subtyping signatures containing rewrite specifications.

The typing rules for abstract and manifest block specifications are the same as for term and module specifications, so we do not repeat them here. It is enough to make ϕ a block B, Ψ a block type BT, and Ξ the block sort blocktype.

1.6.2 Characteristics of the calculus

The way to enter a rewrite specification in an environment is by first using the Formation rule for RewSpec(), making sure it is correct

E ⊢ RewSpec(E^{Γ}, R) : blocktype

Then, using the abstract specification Formation rule, a correct specification is constructed

E ⊢ Ty(RewSpec(E^{Γ}, R)) : spec

Finally, the environment is extended by using the environment rule Insert, yielding

E, \overline{v} : Ty(RewSpec(E^{Γ}, R)) ⊢ ok

Adding an inductive definition to the environment is similar.

We now show how to use a rewrite specification from the environment. Let us call E′ an environment which is an arbitrary extension of the one above. We assume that we have E′ ⊢ ok. The first step consists in using the environment Lookup rule to derive

E′ ⊢ \overline{v} : Ty(RewSpec(E^{Γ}, R))

Second, we apply the abstract specification Elimination rule, yielding

E′ ⊢ \overline{v} : RewSpec(E^{Γ}, R)

Now we can either use the Function rule to get the type of some v^{j} ∈ \overline{v} or the Computation rule to derive a convertibility judgment. Suppose R is our rewriting system defining plus. In that case \overline{v} is a sequence of length 1. Suppose, we want to use the second rewrite rule from R, i.e. plus (S x) y ⇒ S (plus x y). To do that, we have to provide a welltyped substitution δ assigning well typed terms to all variables from the local environment of the rule E_{2} = x : Ty(nat); y : Ty(nat). Let δ = {x ↦ O; y ↦ O}. The substitution δ is well-typed, because E′ ⊢ O : nat. Applying the Computation rule yields:

E′ ⊢ v (S O) O ≈ S (v O O)

Note that our formal system does not permit to orient the equality obtained by the Computation rule. In presence of the Satisfaction rule, naive orientation from left to right would inevitably lead to nontermination. Let us consider the following example: let E^{Γ} = f : Ty(nat → nat) and let R_{1} = {f O ⟶ f (S O); f (S x) ⟶ O} and R_{2} = {f (S x) ⟶ f O; f O ⟶ O} and suppose both rewriting systems satisfy the acceptance condition ACC. In the environment E containing v : Ty(RewSpec(Γ, R_{1})), we have E ⊢ v : RewSpec(E^{Γ}, R_{1}) by environment Lookup followed by abstract specification Elimination. Hence we can derive

E ⊢ v O ≈ v (S O)

On the other hand, using Satisfaction, it is possible to derive E ⊢ v : RewSpec(E^{Γ}, R_{2}), because both equalities E; x : Ty(nat) ⊢ v (S x) ≈ v O and E ⊢ v O ≈ O are satisfied. Hence we can derive

E ⊢ v (S O) ≈ v O

If both displayed equalities were oriented from left to right, we would have a loop.

It seems that the correct way of defining the convertibility relation by oriented reduction rules is via a derivation system similar to the one for δ-reduction (Definition 1.5.1). However, since we do not study the anonymous system in this thesis, we leave the question unanswered.

Rewriting in structures. Inside a structure, a definition by rewriting is composed of a sequence of labels, a sequence of binders, a specification and an implementation. The already mentioned informal structure defining addition on natural numbers:

  Struct
    Symbol plus : nat → nat → nat
    Rules
      plus O y ⇒ y
      plus (S x) y ⇒ S (plus x y)
      plus x O ⇒ x
      plus x (S y) ⇒ S (plus x y)
      plus x (plus y z) ⇒ plus (plus x y) z
  End

is represented in the formal syntax by the structure:

Struct plus▷plus : Ty(RewSpec(E^{Γ}, R)) := Rew(E^{Γ}, R) End

whose signature is

Sig plus▷plus : Ty(RewSpec(E^{Γ}, R)) End

where E^{Γ} and R are as before. The well-typedness derivation of this structure uses the Definition rule for rewrite specification and the completeness condition COMP_{E}(E^{Γ}, R) is necessarily verified. This condition, which is left unspecified like ACC and POS, is supposed to make sure that all functions declared in E^{Γ} and defined by the rewrite rules R are total.

The completeness condition permits to maintain the correspondence between structures and closed environments. In other words, the judgment E ⊢ RewSpec(E^{Γ}, R) : blocktype, means that the specification is correct while the judgment E ⊢ Rew(E^{Γ}, R) : RewSpec(E^{Γ}, R) means that the specification is satisfiable.

From the practical point of view, the condition ACC_{E}(E^{Γ}, R) should imply decidability of type-checking in E′ = E, v : Ty(RewSpec(E^{Γ}, R)), while the condition COMP_{E}(E^{Γ}, R) should imply the consistency of E′, both properties relative with respect to E.

The completeness of definitions in presence of dependent types have been studied in [27]. The topic of completeness of definitions and consistency of the calculus with rewriting is not yet very well studied but there are already some results on this subject [12].

Generativity of definitions. The specifications Ty() and Eq() may be used to control the generativity of definitions. A definition of v is called generative, if it is impossible to write an expression equal to v, without explicitly mentioning the name v. We will illustrate this by examples.

In the first example, we discuss different specifications given for a module containing an inductive definition of natural numbers. To shorten the presentation, we write as usual

E^{I} = nat : Ty(⋆)
E^{C} = 0 : Ty(nat); S : Ty(nat → nat)

Let us consider a structure m containing a definition of natural numbers (it will be given later) and let us now consider the following definitions, each having the same implementation m:

N_{0} : Ty( Sig nat : Ty(⋆); 0 : Ty(nat); S : Ty(nat → nat) End ) := m
N_{1} : Ty( Sig nat, O, S : Ty(IndSpec(E^{I} := E^{C})) End ) := m
N′_{1} : Ty( Sig nat, O, S : Ty(IndSpec(E^{I} := E^{C})) End ) := m
N_{2} : Ty( Sig nat, O, S : Eq(Ind(E^{I} := E^{C}) : IndSpec(E^{I} := E^{C})) End ) := m
N′_{2} : Ty( Sig nat, O, S : Eq(Ind(E^{I} := E^{C}) : IndSpec(E^{I} := E^{C})) End ) := m

In the first definition, N_{0}.nat is not declared as an inductive type. We only know that it is a type. For the next two, N_{1}.nat and N′_{1}.nat are two generative inductive definitions. Because their respective specifications do not tell what they are equal to, each of them is only equal to itself. Therefore, N_{1}.nat ≉ N′_{1}.nat. For the last two, N_{2}.nat and N′_{2}.nat are two non-generative inductive types, both convertible with Ind(E^{I} := E^{C}).1 and therefore with each other.

In order to satisfy all of the above specifications the structure m must be defined as follows:

Struct nat, O, S : Eq(Ind(E^{I} := E^{C}) : IndSpec(E^{I} := E^{C})) := Ind(E^{I} := E^{C}) End

Analogous variations of specifications are also possible for definitions by rewriting. Let us again use the example of plus and the structure m defining it:

Struct plus : Eq(Rew(E^{Γ}, R) : RewSpec(E^{Γ}, R) := Rew(E^{Γ}, R) End

as well as five possible specifications given to this implementation:

M_{0} : Ty( Sig plus : Ty(nat → nat → nat) End ) := m M_{1} : Ty( Sig plus : Ty(RewSpec(E^{Γ}, R)) End ) := m M′_{1} : Ty( Sig plus : Ty(RewSpec(E^{Γ}, R)) End ) := m M_{2} : Ty( Sig plus : Eq(Rew(E^{Γ}, R) : RewSpec(E^{Γ}, R)) End ) := m M′_{2} : Ty( Sig plus : Eq(Rew(E^{Γ}, R) : RewSpec(E^{Γ}, R)) End ) := m

Suppose there are three variables a, b and c of type nat. We will consider the term M.plus a (M.plus b c), with M substituted subsequently by M_{0}, M_{1}, …, M′_{2}. As with inductive types, the first specification is too restrictive and consequently the term M_{0}.plus a (M_{0}.plus b c) is only equal to itself. When substituting any of the other four specifications to M, the term M.plus a (M.plus b c) is equal to M.plus (M.plus a b) c. Now, consider M_{1}.plus a (M′_{1}.plus b c). Since both definitions are generative, this term is only equal to itself. On the other hand, because M_{2} and M′_{2} have manifest specifications, M_{2}.plus a (M′_{2}.plus b c) is convertible with Rew(E^{Γ}, R).1 a (Rew(E^{Γ}, R).1 b c) and therefore with Rew(E^{Γ}, R).1 (Rew(E^{Γ}, R).1 a b) c.

Rewriting and functors Rewrite specifications used in the parameter interface of a functor may be used to impose convertibility constraints on the functor arguments. Before presenting the actual example, let us suppose that P : nat → ⋆ is a predicate, and let us define the following two abbreviations:

BT = RewSpec(f : Ty(nat → nat); g : Ty(nat → nat),
n:Ty(nat) | f (g n) ⟶ g (f n) : nat )
B = Rew(h:Ty(nat → nat), m:nat | h m ⟶ S(S m) : nat)

Let us now consider the following functor:

Functor(X : Sig f,g : Ty(BT ) End)

Struct

lemma : Ty( (n : nat)P (X.f(X.f(X.g n))) → P (X.g(X.f(X.f n))) )

:= [n : nat][p : P (X.f(X.f(X.g n)))] p

End

In the functor body, type-checking the implementation of its single lemma component relies on the rewrite rule placed in the parameter interface. Thanks to the notion of subtyping, the functor presented above can be applied to the structure

Struct g : Eq( [n : nat](S n) : nat → nat ) := [n : nat](S n) f : Eq( B.1 : nat → nat ) := B.1

End In fact, the Satisfaction typing rule allows any sequence of function symbols to satisfy the specification RewSpec(E^{Γ}, R) as long as the corresponding conversion can prove equalities of left-hand and right-hand sides of all rules in R. In our example, the structure defines the functions f and g in such a way that both f(g n) and g(f n) are convertible with S(S(S n)), and therefore the equation expressed by the rule is satisfied.

1.6.3 Closure properties for rewriting

This section illustrates how reductions on anonymous module expressions may interfere with definitions by rewriting. Consider the following functor

Functor[X : Sig a : Ty(nat) End]

Struct

f : Ty( RewSpec(f : Ty(nat → nat), x:Ty(nat) | f x ⟶ X.a : nat) ) := … End

Its application to the structure Struct a : Eq(O : nat) := O End β_{m}-reduces to

Struct

f : Ty( RewSpec(f : Ty(nat → nat),

x:Ty(nat) | f x ⟶ Struct a : Eq(O : nat) := O End.a : nat ) ) := …

End

which in turn, since a has a manifest specification, reduces to

Struct

f : Ty( RewSpec(f : Ty(nat → nat), x:Ty(nat) | f x ⟶ O : nat) ) := … End

Note that if a were itself defined by rewriting, the reduction would lead to a nested definition by rewriting, quite an unusual feature.

In general, the subject reduction property implies that the conditions ACC() and COMP() for accepting the rewrite rules must be closed under substitution and reduction. These closure properties unfortunately do not hold for the currently known methods for proving termination or confluence [3, 12, 99]. One must therefore either restrict the lefthand and right-hand sides of the rules in such a way that substitutions and reductions are impossible or put limitations on the calculus in order to relax the closure requirements for the conditions on rewrite rules. In the remaining part of this thesis, we adopt the second possibility.

Note also that this is not the anonymous definitions by rewriting per se which impose these strong closure properties on the acceptance conditions, but the fact that definitions by rewriting occur inside anonymous modules. The simplest way to limit the impact of the module calculus on the definitions by rewriting is to get rid of anonymous modules and their reductions.

1.7 Named modules

The calculus studied in this thesis is obtained by imposing several syntactic restrictions on the calculus of anonymous modules presented in the previous section. The most important restriction is the necessity to name all complex module expressions before using them in terms. In this way, the language of terms will not contain the general module selection expression m.v, but only so called access paths consisting of possibly nested selections of a field from a module variable, i.e. v.v_{1}. · · · .v_{n}. This restriction is actually present in many module systems used in functional programming languages (see for example [59]).

Another restriction is the elimination of anonymous inductive types and anonymous definitions by rewriting. From a syntactic point of view, we do not allow expressions like B.i where B is a block, and we allow only sequences of access paths in manifest block specifications, as in Eq(\overline{p} : BT) where \overline{p} denotes a sequence of access paths. Consequently, anonymous block expressions Ind() and Rew() are only allowed as implementations of their respective specifications inside structures. Inductive definitions and definitions by rewriting become therefore generative, and behave in the same way as the ones presented in Sections 1.3 and 1.4.

As a consequence of these syntactic restrictions, there are no more module reductions. A β_{m}-redex is not even a correct module expression, as the application of modules is restricted to application of access paths.

1.8 Contributions

In this thesis we show that adding named modules, as briefly described in the previous section, to a pure type system with generative inductive definitions and definitions by rewriting does not compromise the system’s consistency and decidability of type-checking.

Assuming that consistency of a calculus with generative definitions means that no closed formal development proving False exists, the conservativity of the modular extension is proved by a procedure transforming any closed development (with modules) into a closed development without modules.

The procedure has two phases: first, all module components of the formal development are evaluated to a weak-head normal form using the call-by-value evaluation strategy; and second, the resulting evaluated development is flattened, i.e. components of modules are promoted to the toplevel, and modules themselves removed.

Since the module language itself forms a typed lambda calculus, termination of the first phase is not immediate. It is shown by translating our module expressions to a weak version of the module calculus of Courant and relating our evaluation steps with reductions between translated expressions. Since the calculus of Courant is terminating, so is our evaluation.

Proving the correctness of the second phase requires showing that module typing rules (subtyping in particular) are not essential when typing terms. We have therefore defined a simplified calculus, called principal, which contains only typing rules for terms and rules to extract term information from the environment. We show that term judgments derivable in both calculi are the same, a result which can also be interpreted as soundness of module subtyping. In the principal system, showing that module flattening preserves typing is an easy induction on the derivation.

The relative decidability proof is based on the observation that type preservation for flattening works in both directions: a flattened term is typable in a flattened environment if and only if the original term is typable in the original environment. Since the flattening operation itself has polynomial complexity, this gives us a polynomial reduction of the problem of typing terms in an arbitrary environment to the typing problem for the principal system in an environment without modules. And since derivations in the latter system correspond to derivations in a PTS with generative definitions, we have the desired relative decidability result.

We also prove that our module language has the principal typing property, which implies decidability of the module typing itself as well, again, under the assumption that typing in the base PTS is decidable.

As we explained in Section 1.4, in order to assure that a (consistent and decidable) pure type system extended with rewriting is consistent and decidable, it is necessary to restrict the class of rewriting systems acceptable in a correct environment. Therefore we assume the existence of an acceptance condition, which acts as a guard of the environment’s correctness: every rewriting system is verified against this condition before being added to the environment and in return the acceptance condition guarantees that type-checking in the correct environment is decidable.

In our thesis, we do not fix any particular acceptance condition. We only list closure properties which have to be verified by such a condition in order to make our proofs work. These properties may be summarized as modularity and substitutivity of names by names and they are compatible with acceptance conditions studied in the literature [3, 11, 99]. In Chapter 11 we discuss our closure properties in detail and propose a condition satisfying them.

Let us remark that our closure properties are much weaker than those which would be necessary for the system with anonymous modules of Courant. In particular we do not need closure by arbitrary substitutions, closure by name substitution (i.e. substituting names to names) being enough. The latter property is much simpler to fulfill in practice, as name substitutions do not change the syntactic form of terms on which syntactic termination criteria for rewriting systems rely [3, 12, 99].

Similarly, we do not impose any particular positivity criterion for inductive definitions. They have to verify the same closure properties as acceptance conditions for rewrite rules, but this is much less problematic.

The main practical result of this thesis is the implementation of the module system in the Coq proof assistant.^{14} Apart from implementing a module type-checker in the kernel of Coq, a considerable implementation effort has been done towards the modularization of extra-logical features of Coq such as databases for automated tactics, customizable parsing and pretty-printing, etc. Part of this thesis describing implementation of modules in Coq has been accepted for presentation at the 16th International Conference on Theorem Proving in Higher Order Logics (TPHOLs) in Rome, Italy, 9-12 September 2003 [19].

14Definitions by rewriting have not yet been implemented however. The experimental “Rewriting” branch of Coq is developed by Frédéric Blanqui, but is not yet provided in the distributed versions of Coq.

Modules are included in the version 7.4 of Coq, officially released in February 2003. Since then, several large developments using modules have been realized, proving their usefulness in proof development.

1.9 Roadmap

The remaining part of this thesis is organized as follows. Chapter 2 contains the definition of our module system: its syntax, typing rules, closure properties imposed on block formation rules (Section 2.7.1) and auxiliary definitions such as positions or bound variables.

Chapter 3 formulates the main result of this thesis, i.e. conservativity (Theorem 1), and informally describes the proof method which takes four consecutive chapters.

Chapter 4 studies basic metatheoretical properties of our calculus such as substitutivity by access paths and correctness of module types, block types and specifications assigned to expressions.

Chapter 5 introduces a restricted version of typing, called principal, which contains only term typing rules and elimination rules for modules and blocks. It is shown that module paths have principal types and that principal derivations are complete in a sense that all term judgments derivable in the full system are also derivable in the principal system.

Chapter 6 contains the details of first part of the proof of Theorem 1. It shows how to evaluate module applications without breaking syntactic restrictions on paths. The call-by-value evaluation is defined and shown to be type preserving and terminating.

Chapter 7 proves the second part of the conservativity theorem, called flattening. It strongly relies on properties of principal derivations.

Chapter 8 shows the reduction of the typing problem for the calculus with modules and manifest blocks to typing in the calculus without modules and without manifest blocks (Theorem 2), showing that modular extension of a decidable theory is decidable.

Chapter 9 describes the implementation of the module system in Coq. Various implementation problems are presented together with adopted solutions.

Chapter 10 presents related work on module systems in ML family of programming languages and structuring facilities in other proof assistants.

Chapter 11 discusses closure properties for acceptance conditions for definitions by rewriting, their informal motivation, practical meaning and proposes acceptance conditions satisfying them.

The last chapter mentions some developments realized with modules and presents future research topics and directions.

Named modules

In this chapter we give the complete mathematical definition of the calculus of named modules implemented in the Coq version 7.4 and studied in this thesis. We start by the syntax, then we give the typing rules and definitions of auxiliary concepts like positions and bound variables.

Our system is a syntactic restriction of the anonymous calculus presented in Section 1.6 in the introduction. In a few words: all modules are named and all inductive and rewrite definitions are generative.

2.1 Syntax

Since our system is an extension of a PTS, we start by giving a set of sorts S, a set of PTS axioms A ⊆ S × S and a set of product rules R ⊆ S × S × S. We assume that ⋆ is an element of S. This assumption will be used in Section 6.3.

• Access path:

p ::= v variable | p.v field selection

• Term^{1}:

e, t ::= p term path | s s ∈ S, a PTS sort | (e_{1} e_{2}) application | [v:t] e abstraction | (v:t_{1}) t_{2} product

• Term specification:

T S ::= Ty(t) abstract

1The two letters e and t correspond to the same syntactic class. The letter t is used in places where a term intuitively plays a role of a type.

| Eq(e : t) manifest

• Module expression: m ::= Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End structure

| Functor[v:M] m functor | p module path | (p_{1} p_{2}) path application

• Module type: M ::= Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End module signature

| Funsig(v:M_{1}) M_{2} functor type

• Module specification: MS ::= Ty(M) abstract

| Eq(p : M) manifest

• Basic block: BB ::= Ind(E^{I} := E^{C}) inductive definition

| Rew(E^{Γ}, R) definition by rewriting

• Block: B ::= BB basic block

| \overline{p} sequence of paths

• Block type: BT ::= IndSpec(E^{I} := E^{C}) inductive specification

| RewSpec(E^{Γ}, R) rewrite specification

• Block specification: BS ::= Ty(BT) abstract

| Eq(\overline{p} : BT) manifest

• Specification: S ::= TS | MS | BS

• Implementation: P ::= e | m | B

• Environment: E ::= ϵ | \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}

• Rewriting System: R ::= {E_{i} | e_{i}^{L} ⟶ e_{i}^{R} : t_{i}}_{i=1...n} A rewriting system is a sequence of rules. We use the syntax with braces for the sake of brevity. An element of either syntactic class presented above besides environments and rewriting systems (E and R) is called an expression. An element which belongs to either of the main syntactic classes presented above or auxiliary syntactic classes presented below is called an extended expression.

The following syntactic classes are used to abbreviate the formulation of some definitions and lemmas:

• Structure Component: StrC ::= \overline{v}▷\overline{v} : S := P

• Structure Component Sequence:

StrCs ::= StrC_{1} … StrC_{n}

• Signature Component:

SigC ::= \overline{v}▷\overline{v} : S

• Signature Component Sequence:

SigCs ::= SigC_{1} … SigC_{n}

• Environment Component:

EnvC ::= \overline{v} : S

• Rewrite Rule:

Rule ::= E | e^{L} ⟶ e^{R} : t

Metavariables in this thesis are always meaningful, i.e. “For all m such that… ” should always be read as “For all module expressions m such that… ” etc. Metavariables different from those listed above are treated less strictly. In particular ψ, ϕ are used to mean expressions, i.e. terms e, modules m or blocks B. The symbols Ψ, Φ denote types: terms t, module types M or block types BT. The symbol Ξ denotes a sort: a PTS sort s, the module sort modtype or the block sort blocktype. These symbols are often used in judgments and rules concerning the specifications of the three kinds: term, module and block.

The auxiliary syntactic function length(·) is equal to the number of terms in blocks, block types and block specifications. It is naturally extended to specifications and environments.

• Blocks length(Rew(E^{Γ}, R)) = length(E^{Γ}) length(Ind(E^{I} := E^{C})) = length(E^{I}) + length(E^{C}) length(p^{1} … p^{n}) = n • Block types

length(RewSpec(E^{Γ}, R)) = length(E^{Γ}) length(IndSpec(E^{I} := E^{C})) = length(E^{I}) + length(E^{C})

• Block specifications length(Ty(BT)) = length(BT) length(Eq(B : BT)) = length(BT)

• Term specifications length(TS) = 1

• Module specifications length(MS) = 1

• Environments length(\overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}) = length(\overline{v}_{1}) + ⋯ + length(\overline{v}_{n})

Given a structure m, we will use the abbreviation SIG(m) to denote a primary signature of the structure m, defined as follows:

SIG(Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End) = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End

2.2 Judgments

There are many forms of judgments in our calculus. We present here the complete list of them, together with the intuitive meaning:

1. E ⊢ ok the environment E is well-formed.

2. E ⊢ Φ_{1} ≈ Φ_{2} in the environment E, Φ_{1} and Φ_{2} are convertible.

The possibilities for Φ_{1} and Φ_{2} are the following:

• E ⊢ e_{1} ≈ e_{2} terms,

• E ⊢ p_{1} ≈ p_{2} access paths,

• E ⊢ BT_{1} ≈ BT_{2} block types,

• E ⊢ \overline{p}_{1} ≈ \overline{p}_{2} path sequences,

• E ⊢ M_{1} ≈ M_{2} module types,

• E ⊢ S_{1} ≈ S_{2} specifications

3. E ⊢ M_{1} <: M_{2} in the environment E the module type M_{1} is a subtype of M_{2}

4. E ⊢ ϕ : Ψ in the environment E, ϕ can be ascribed with Ψ.

The possibilities for ϕ and Ψ are the following:

• E ⊢ e : t term e has type t

• E ⊢ e : TS term e verifies term specification TS

• E ⊢ m : M module expression m has module type M

• E ⊢ M : modtype module type M is well-formed

• E ⊢ m : MS module expression m verifies module specification MS,

• E ⊢ B : BT block B has block type BT

• E ⊢ BT : blocktype block type BT is correct

• E ⊢ B : BS block B verifies block specification BS

• E ⊢ S : spec specification S is well-formed

The judgment E ⊢ \overline{p}_{1} ≈ \overline{p}_{2} should be read as an abbreviation for a sequence of judgments E ⊢ p_{1}^{1} ≈ p_{2}^{1} … E ⊢ p_{1}^{n} ≈ p_{2}^{n}.

The judgments E ⊢ M_{1} ≈ M_{2} and E ⊢ S_{1} ≈ S_{2} are not used in any conversion rule. They are only used in proofs.

It is possible to classify our judgments differently, into three main categories of terms, modules and blocks and two auxiliary ones: the environment correctness judgment E ⊢ ok and specification correctness judgment E ⊢ S : spec. Each of the main three categories have ascription judgments E ⊢ ϕ : Ψ and convertibility judgments E ⊢ ϕ_{1} ≈ ϕ_{2}. Ascription judgments are: an object has a type, a type has a sort (is correct), an object verifies a specification. Convertibility judgments are for objects and for their types. In case of terms the first two ascription judgments collapse, as well as the convertibility judgments. Modules have one additional judgment concerning subtyping, and their object convertibility is reduced to access paths. Of course the typing judgments like E ⊢ M : modtype (or E ⊢ BT : blocktype or E ⊢ S : spec) could have been presented as specialized judgments of the form E ⊢ well-formed-module-type(M) but we chose the typing-like presentation. In case of modtype and blocktype it makes it easier to write rules for specifications and in the case of spec it is an arbitrary decision.

2.3 Environment

Variables in environments are assumed to be pairwise disjoint, so in the second rule \overline{v} are fresh variables and length(\overline{v}) = length(S).

(Env/Empty)  \overline{ϵ ⊢ ok}
(Env/Insert) \frac{E ⊢ ok E ⊢ S : spec}{E; \overline{v} : S ⊢ ok}  (Env/Lookup) \frac{E_{1}; \overline{v} : S; E_{2} ⊢ ok}{E_{1}; \overline{v} : S; E_{2} ⊢ \overline{v} : S}

2.4 Terms

The rules in this section correspond to the typing rules for pure type systems.

(Term/Prod) \frac{E ⊢ t_{1} : s_{1} E; v : Ty(t_{1}) ⊢ t_{2} : s_{2}}{E ⊢ (v:t_{1}) t_{2} : s_{3}} if (s_{1}, s_{2}, s_{3}) ∈ R

(Term/Abs)  \frac{E; v : Ty(t_{1}) ⊢ e : t_{2} E ⊢ (v:t_{1}) t_{2} : s}{E ⊢ [v:t_{1}]e : (v:t_{1}) t_{2}}

(Term/Axiom) \frac{E ⊢ ok}{E ⊢ s_{1} : s_{2}} if (s_{1}, s_{2}) ∈ A

(Term/App)  \frac{E ⊢ e : (v:t_{1}) t_{2} E ⊢ e′ : t_{1}}{E ⊢ e e′ : t_{2}\{v ↦ e′\}}
(Term/Beta) [v:t]e e′ ⟶_{β} e{v ↦ e′}

(Term/Conv)  \frac{E ⊢ e : t E ⊢ t′ : s E ⊢ t ≈ t′}{E ⊢ e : t′}

2.5 Modules

The module rules are almost identical to those defined in [32] and presented in Section 1.5. Only the rules for signature access and module application are restricted to paths. Also, since module expressions are not part of terms any more and hence are not concerned by the conversion rule, module β_{m} reduction is no longer needed.

Structures and signatures

(Sig/Form)  \frac{E; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ ok}{E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End : modtype}
if \overline{v}_{1} … \overline{v}_{n} are pairwise disjoint labels
and length(\overline{v}_{i}) = length(\overline{v}_{i}) for all i

(Sig/Struct)

E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End : modtype
E; \overline{v}_{1} : S_{1} … \overline{v}_{k−1} : S_{k−1} ⊢ P_{k} : S_{k} for k = 1 … n

E ⊢ Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End : Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End The access rule is restricted to module paths.

(Sig/Access) (E ⊢ p : Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End) / (E ⊢ \overline{p.v_{k}} : S_{k}\{v_{i} ↦ p.v_{i}\}_{i=1...k−1})

(Sig/Sub)

E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End : modtype
E ⊢ Sig \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} … \overline{v′}_{n′}▷\overline{v′}_{n′} : S′_{n′} End : modtype
\{\overline{v′}_{1} … \overline{v′}_{n′}\} ⊆ \{\overline{v}_{1} … \overline{v}_{n}\}
by α-conversion, we assume v′_{i′}^{j′} = v_{i}^{j} implies v′_{i′}^{j′} = v_{i}^{j}
E; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ \overline{v′}_{k} : S′_{k} for k = 1 … n′
E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End <: Sig \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} … \overline{v′}_{n′}▷\overline{v′}_{n′} : S′_{n′} End

Functors and functor types Functors are exactly like in ML-like module systems. They correspond to functions and their types to products. They can be higher order and their subtyping is contravariant in the argument, but covariant in the output type.

(Funsig/Form)  \frac{E ⊢ M_{1} : modtype E; v : Ty(M_{1}) ⊢ M_{2} : modtype}{E ⊢ Funsig(v:M_{1}) M_{2} : modtype}

(Funsig/Functor)  \frac{E; v : Ty(M_{1}) ⊢ m : M_{2} E ⊢ Funsig(v:M_{1})M_{2} : modtype}{E ⊢ Functor[v:M_{1}] m : Funsig(v:M_{1})M_{2}}

(Funsig/Sub)  \frac{E ⊢ M′_{1} <: M_{1} E; v : Ty(M′_{1}) ⊢ M_{2} <: M′_{2}}{E ⊢ Funsig(v:M_{1}) M_{2} <: Funsig(v:M′_{1}) M′_{2}}

Like the access rule, the application rule is restricted to paths.

(Mod/App)  \frac{E ⊢ p : Funsig(v : M_{1}) M_{2} E ⊢ p′ : M_{1}}{E ⊢ p p′ : M_{2}\{v ↦ p′\}}

The computation rule β_{m} is no longer needed.

Other rules

(Subsumption)  \frac{E ⊢ m : M_{1} E ⊢ M_{1} <: M_{2}}{E ⊢ m : M_{2}}

(Strengthening)  \frac{E ⊢ p : M}{E ⊢ p : M/p}

where the strengthening operation ·/p is defined for module types, specifications, block types and terms as follows:

Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End/p = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1}/\overline{p.v_{1}}; …; \overline{v}_{n}▷\overline{v}_{n} : S_{n}/\overline{p.v_{n}} End
(Funsig(v:M_{1}) M_{2})/p = Funsig(v:M_{1}) M_{2}
Eq(\overline{ϕ} : Ψ)/\overline{p} = Eq(\overline{ϕ} : Ψ/\overline{p})
Ty(Ψ)/\overline{p} = Eq(\overline{p} : Ψ/\overline{p})
BT /\overline{p} = BT
t/p = t

2.6 Specifications

The following rules define abstract and manifest specifications. An abstract specification Ty(Ψ) asserts that a given object has type Ψ (or more generally: properties defined by Ψ). A manifest specification Eq(ϕ : Ψ) asserts that a given object has type Ψ and is equal to ϕ. The formation rule guarantees of course that ϕ itself of type Ψ. Since the rules for abstract/manifest specifications are the same for terms, modules and blocks we give them only once. In the following rules triples ϕ, Ψ, Ξ are e, t, s or p, M, modtype or

\overline{p}, BT , blocktype.

Abstract specifications

(Ty/Form)  \frac{E ⊢ Ψ : Ξ}{E ⊢ Ty(Ψ) : spec}
(Ty/Sat)  \frac{E ⊢ P : Ψ E ⊢ Ty(Ψ) : spec}{E ⊢ P : Ty(Ψ)}
(Ty/Type)  \frac{E ⊢ P : Ty(Ψ)}{E ⊢ P : Ψ}

Manifest specifications

(Eq/Form)  \frac{E ⊢ ϕ : Ψ E ⊢ Ψ : Ξ}{E ⊢ Eq(ϕ : Ψ) : spec}

(Eq/Sat)  \frac{E ⊢ P ≈ ϕ E ⊢ P : Ψ E ⊢ Eq(ϕ : Ψ) : spec}{E ⊢ P : Eq(ϕ : Ψ)}
(Eq/Type)  \frac{E ⊢ P : Eq(ϕ : Ψ)}{E ⊢ P : Ψ}
(Eq/Comp)  \frac{E ⊢ P : Eq(ϕ : Ψ)}{E ⊢ P ≈ ϕ}

2.7 Blocks

Block rules are almost identical to those defined is Section 1.6. The only difference is that rules for block type satisfaction, elimination and computation are restricted to path sequences.

Inductive blocks Let E^{I} = v_{1}^{I} : Ty(t_{1}) … v_{k}^{I} : Ty(t_{k}), E^{C} = v_{1}^{C} : Ty(t̂_{1}) … v_{n}^{C} : Ty(t̂_{n})

(IndSpec/Form) \frac{E ⊢ t_{j} : s_{j} for j = 1 … k E; E^{I} ⊢ t̂_{i} : s′_{i} for i = 1 … n}{E ⊢ IndSpec(E^{I} := E^{C}) : blocktype} if POS_{E}(E^{I} := E^{C})

(IndSpec/Ind)  \frac{E ⊢ IndSpec(E^{I} := E^{C}) : blocktype}{E ⊢ Ind(E^{I} := E^{C}) : IndSpec(E^{I} := E^{C})}

(IndSpec/IndType) \frac{E ⊢ \overline{p} : IndSpec(E^{I} := E^{C})}{E ⊢ p^{j} : t_{j}} for j = 1 … k

(IndSpec/Constr) \frac{E ⊢ \overline{p} : IndSpec(E^{I} := E^{C})}{E ⊢ p^{k+i} : t̂_{i}\{v_{j}^{I} ↦ p^{j}\}_{j=1...k}} for i = 1 … n

Rewrite blocks In the forthcoming rules E^{Γ} = v_{1} : Ty(t_{1}) … v_{n} : Ty(t_{n}) and R = {E_{i} | e_{i}^{L} ⟶ e_{i}^{R} : t̂_{i}}_{i=1...m}, where E_{i} = v_{1}^{i} : Ty(t_{1}^{i}); …; v_{n_{i}}^{i} : Ty(t_{n_{i}}^{i})

(RewSpec/Form)

\frac{E ⊢ t_{k} : s_{k} for k = 1 … n \begin{matrix}E; v_{1}^{i} : Ty(t_{1}^{i}); …; v_{j−1}^{i} : Ty(t_{j−1}^{i}) ⊢ t_{j}^{i} : s_{j}^{i} for j = 1 … n_{i} \\ E; E^{Γ}; E_{i} ⊢ t̂_{i} : ŝ_{i} E; E^{Γ}; E_{i} ⊢ e_{i}^{L} : t̂_{i} E; E^{Γ}; E_{i} ⊢ e_{i}^{R} : t̂_{i} \end{matrix} \} for i = 1 … m}{E ⊢ RewSpec(E^{Γ}, R) : blocktype}

if ACC_{E}(E^{Γ}, R)

(RewSpec/Rew) \frac{E ⊢ RewSpec(E^{Γ}, R) : blocktype}{E ⊢ Rew(E^{Γ}, R) : RewSpec(E^{Γ}, R)} if COMP_{E}(E^{Γ}, R)

(RewSpec/Fun)  \frac{E ⊢ \overline{p} : RewSpec(E^{Γ}, R)}{E ⊢ p^{j} : t_{j}}

(RewSpec/Comp)

\frac{E ⊢ \overline{p} : RewSpec(E^{Γ}, R) δ : E_{i} → E}{E ⊢ e_{i}^{L}θδ ≈ e_{i}^{R}θδ} where θ = \{v_{j} ↦ p^{j}\}_{j=1...n}

The notation δ : E_{i} → E means that E ⊢ vδ : tδ holds for every v : Ty(t) ∈ E_{i}.

(RewSpec/Sat) \frac{E ⊢ RewSpec(E^{Γ}, R) : blocktype E ⊢ p^{j} : t_{j} for j = 1 … n E; E_{i} ⊢ e_{i}^{L}θ ≈ e_{i}^{R}θ for i = 1 … m}{E ⊢ \overline{p} : RewSpec(E^{Γ}, R)} where θ = \{v_{j} ↦ p^{j}\}_{j=1...n}

Block conversion

(Block/Conv)  \frac{E ⊢ B : BT  E ⊢ BT′ : blocktype E ⊢ BT ≈ BT′}{E ⊢ B : BT′}

2.7.1 Closure properties

Each one of the side-conditions C present in the above rules (POS, ACC and COMP) must satisfy the following closure properties:

C_{E}(Ω)  implies  C_{E′}(Ω′)  if:
(C1) E = E_{1}; \overline{v} : S; E_{2}  E′ = E_{1}; E_{2}\{\overline{v} ↦ \overline{p}\}  with E_{1} ⊢ \overline{p} : S
Ω  Ω′ = Ω\{\overline{v} ↦ \overline{p}\},
(C2) E = E_{1}; E_{3}  E′ = E_{1}; E_{2}; E_{3}
Ω  Ω′ = Ω
(C3) E = E_{1}; v : Ty(Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End); E_{2}

Ω E′ = E_{1}; \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n}; E_{2}\{\overline{v.v}_{i} ↦ \overline{v}_{i}\}

Ω′ = Ω\{\overline{v.v}_{i} ↦ \overline{v}_{i}\}
(C4) E = E_{1}; v : Ty(Funsig(v̂ : M)M′); E_{2}

Ω E′ = E_{1}; E_{2}

Ω′ = Ω
(C5) E = E_{1}; v : Eq(p : M); E_{2} E′ = E_{1}; v : Ty(M/p); E_{2}
Ω  Ω′ = Ω

In all of the above implications it is assumed that both environments E and E′ are correct. Moreover in the last three properties, E_{2} is an environment without modules.

In short, these properties require the side-conditions to be modular and closed under arbitrary path substitutions. They are discussed in details in Chapter 11.

2.8 Convertibility

Convertibility is a congruence based on the equality judgments derivable from the rules (Eq/Comp) and (RewSpec/Comp) and from the rule (Conv/Beta) below. All other rules are needed to make it a congruence. The convertibility relation is used in typing derivations through the rules (Block/Conv) and (Term/Conv). There are no such rules for module types and specification, as they can be deduced from others.

General

(Conv/Trans) \frac{E ⊢ ϕ_{1} ≈ ϕ_{2} E ⊢ ϕ_{2} ≈ ϕ_{3}}{E ⊢ ϕ_{1} ≈ ϕ_{3}}  (Conv/Sym) \frac{E ⊢ ϕ_{1} ≈ ϕ_{2}}{E ⊢ ϕ_{2} ≈ ϕ_{1}}

where ϕ_{1}, ϕ_{2}, ϕ_{3} are terms or paths. Paths

(Conv/Refl)  \frac{E ⊢ v : M}{E ⊢ v ≈ v}
(Conv/Path)  \frac{E ⊢ p.v : Ψ E ⊢ p ≈ p′ E ⊢ p′.v : Ψ′}{E ⊢ p.v ≈ p′.v}

where Ψ and Ψ′ are module types or terms. Terms

(Conv/Beta)  \frac{E ⊢ e_{1} : t e_{1} =_{β} e_{2} E ⊢ e_{2} : t′}{E ⊢ e_{1} ≈ e_{2}}
(Conv/App)  \frac{E ⊢ e_{1} e_{2} : t E ⊢ e_{1} ≈ e′_{1} E ⊢ e_{2} ≈ e′_{2} E ⊢ e′_{1} e′_{2} : t′}{E ⊢ e_{1} e_{2} ≈ e′_{1} e′_{2}}

(Conv/Prod)

\frac{E ⊢ (v:t_{1}) t_{2} : t E ⊢ t_{1} ≈ t′_{1} E; v : Ty(t_{1}) ⊢ t_{2} ≈ t′_{2} E ⊢ (v:t′_{1}) t′_{2} : t′}{E ⊢ (v:t_{1}) t_{2} ≈ (v:t′_{1}) t′_{2}}

(Conv/Abs)

\frac{E ⊢ [v:t]e : t̂ E ⊢ t ≈ t′ E; v : Ty(t) ⊢ e ≈ e′ E ⊢ [v:t′]e′ : t̂′}{E ⊢ [v:t]e ≈ [v:t′]e′}

Block types

(Conv/IndSpec)  \frac{E ⊢ IndSpec(E^{I} := E^{C}) : blocktype E ⊢ t_{j} ≈ t′_{j} v_{j}^{I} = v^{I} ′_{j} for j = 1 … k E; E^{I} ⊢ t̂_{i} ≈ t̂_{i}′ v_{i}^{C} = v^{C} ′_{i} for i = 1 … n E ⊢ IndSpec(E^{I}′ := E^{C}′) : blocktype}{E ⊢ IndSpec(E^{I} := E^{C}) ≈ IndSpec(E^{I}′ := E^{C}′)}

where E^{I} = v_{1}^{I} : Ty(t_{1}) … v_{k}^{I} : Ty(t_{k}) E^{I}′ = v_{1}^{I} : Ty(t′_{1}) … v_{k}^{I} : Ty(t′_{k}) E^{C} = v_{1}^{C} : Ty(t̂_{1}) … v_{n}^{C} : Ty(t̂_{n}) E^{C}′ = v_{1}^{C} : Ty(t̂′_{1}) … v_{n}^{C} : Ty(t̂′_{n})

(Conv/RewSpec)

\frac{E ⊢ RewSpec(E^{Γ}, R) : blocktype E ⊢ t_{k} ≈ t′_{k} for k = 1 … n \begin{matrix}E; v_{1}^{i} : Ty(t_{1}^{i}); …; v_{j−1}^{i} : Ty(t_{j−1}^{i}) ⊢ t_{j}^{i} ≈ t_{j}^{′i} for j = 1 … n_{i} \\ E; E^{Γ}; E_{i} ⊢ e_{i}^{L} ≈ e_{i}^{L}′ E; E^{Γ}; E_{i} ⊢ e_{i}^{R} ≈ e_{i}^{R}′ E; E^{Γ}; E_{i} ⊢ t̂_{i} ≈ t̂_{i}′ \end{matrix} \} for i = 1 … m E ⊢ RewSpec(E^{Γ}′, R′) : blocktype}{E ⊢ RewSpec(E^{Γ}, R) ≈ RewSpec(E^{Γ}′, R′)}

where E^{Γ} = v_{1} : Ty(t_{1}) … v_{n} : Ty(t_{n}) E^{Γ}′ = v_{1} : Ty(t′_{1}) … v_{n} : Ty(t′_{n}) R = {E_{i} | e_{i}^{L} ⟶ e_{i}^{R} : t̂_{i}}_{i=1...m} R′ = {E′_{i} | e_{i}^{L}′ ⟶ e_{i}^{R}′ : t̂_{i}′}_{i=1...m} E_{i} = v_{1}^{i} : Ty(t_{1}^{i})...v_{n_{i}}^{i} : Ty(t_{n_{i}}^{i}) E′_{i} = v_{1}^{i} : Ty(t_{1}^{i}′)...v_{n_{i}}^{i} : Ty(t_{n_{i}}^{i}′) Specifications The judgments E ⊢ S_{1} ≈ S_{2} and E ⊢ M_{1} ≈ M_{2} are not used in conversion rules. They are only used in proofs.

(Conv/Ty)  \frac{E ⊢ Ty(Ψ) : spec E ⊢ Ψ ≈ Ψ′ E ⊢ Ty(Ψ′) : spec}{E ⊢ Ty(Ψ) ≈ Ty(Ψ′)}

(Conv/Eq)

\frac{E ⊢ Eq(ϕ : Ψ) : spec E ⊢ ϕ ≈ ϕ′ E ⊢ Ψ ≈ Ψ′ E ⊢ Eq(ϕ′ : Ψ′) : spec}{E ⊢ Eq(ϕ : Ψ) ≈ Eq(ϕ′ : Ψ′)}

Module types (Conv/Sig)

E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End : modtype
E; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ S_{i} ≈ S′_{i} for i = 1 … n
E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S′_{n} End : modtype

E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End ≈ Sig \overline{v}_{1}▷\overline{v}_{1} : S′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S′_{n} End

(Conv/Funsig)  \frac{E ⊢ Funsig(v:M_{1}) M_{2} : modtype E ⊢ M_{1} ≈ M′_{1} E; v : Ty(M_{1}) ⊢ M_{2} ≈ M′_{2} E ⊢ Funsig(v:M′_{1}) M′_{2} : modtype}{E ⊢ Funsig(v:M_{1}) M_{2} ≈ Funsig(v:M′_{1}) M′_{2}}

2.9 Positions, subexpressions and bound variables

In this section we define positions and subexpressions of an expression.

Definition 2.9.1. The set of positions of an extended expression ψ, denoted by P os(ψ), is defined as follows (the left column is to indicate the syntactic class of a given ψ):

p  P os(v) = {ϵ}
P os(p.v) = {ϵ} ∪ 1 · P os(p)
e, t  P os(s) = {ϵ}
P os(e_{1} e_{2}) = {ϵ} ∪ 1 · P os(e_{1}) ∪ 2 · P os(e_{2})
P os([v:t] e) = {ϵ} ∪ 1 · P os(t) ∪ 2 · P os(e)
P os((v:t_{1}) t_{2}) = {ϵ} ∪ 1 · P os(t_{1}) ∪ 2 · P os(t_{2})
S  P os(Ty(Ψ)) = {ϵ} ∪ 1 · P os(Ψ)
P os(Eq(ϕ : Ψ)) = {ϵ} ∪ 1 · P os(ϕ) ∪ 2 · P os(Ψ)

m P os(Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End) =

\{ϵ\} ∪ 1 · P os(\overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1}) ∪ ⋯ ∪ n · P os(\overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n})

P os(Functor[v:M] m) = {ϵ} ∪ 1 · P os(M) ∪ 2 · P os(m)
P os(p_{1} p_{2}) = {ϵ} ∪ 1 · P os(p_{1}) ∪ 2 · P os(p_{2})
StrC  P os(\overline{v}▷\overline{v} : S := P) = \{ϵ\} ∪ 1 · P os(S) ∪ 2 · P os(P)

M P os(Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End) =

\{ϵ\} ∪ 1 · P os(\overline{v}_{1}▷\overline{v}_{1} : S_{1}) ∪ ⋯ ∪ n · P os(\overline{v}_{n}▷\overline{v}_{n} : S_{n})

P os(Funsig(v:M_{1}) M_{2}) = {ϵ} ∪ 1 · P os(M_{1}) ∪ 2 · P os(M_{2})
SigC  P os(\overline{v}▷\overline{v} : S) = \{ϵ\} ∪ 1 · P os(S)
B  P os(Ind(E^{I} := E^{C})) = {ϵ} ∪ 1 · P os(E^{I}) ∪ 2 · P os(E^{C})
P os(Rew(E^{Γ}, R)) = {ϵ} ∪ 1 · P os(E^{Γ}) ∪ 2 · P os(R)
P os(p^{1} … p^{n}) = {ϵ} ∪ 1 · P os(p_{1}) ∪ ⋯ ∪ n · P os(p_{n})
BT  P os(IndSpec(E^{I} := E^{C})) = {ϵ} ∪ 1 · P os(E^{I}) ∪ 2 · P os(E^{C})
P os(RewSpec(E^{Γ}, R)) = {ϵ} ∪ 1 · P os(E^{Γ}) ∪ 2 · P os(R)
E  P os(\overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}) = \{ϵ\} ∪ 1 · P os(\overline{v}_{1} : S_{1}) ∪ ⋯ ∪ n · P os(\overline{v}_{n} : S_{n})
EnvC  P os(\overline{v} : S) = \{ϵ\} ∪ 1 · P os(S)
R  P os({E_{j} | e_{j}^{L} ⟶ e_{j}^{R} : t_{j}}_{j=1...n}) =

{ϵ} ∪ 1 · P os(E_{1} | e_{1}^{L} ⟶ e_{1}^{R} : t_{1}) ∪ ⋯ ∪ n · P os(E_{n} | e_{n}^{L} ⟶ e_{n}^{R} : t_{n})

Rule  P os(E | e^{L} ⟶ e^{R} : t) =

{ϵ} ∪ 1 · P os(E) ∪ 2 · P os(e^{L}) ∪ 3 · P os(e^{R}) ∪ 4 · P os(t)

where i · A = {i · w | w ∈ A}

If one treats an extended expression ψ like a labelled tree, the skeleton of which is the set of positions P os(ψ), then the subtree rooted at position q is denoted by ψ|_{q}. Those subtrees are called subexpressions.

Definition 2.9.2. Given an extended expression ψ and a position q ∈ P os(ψ), the environment of bound variables of ψ at position q, denoted by BV (ψ, q), is defined as follows:

p  BV (p, q) = ϵ
e, t  BV (s, q) = ϵ
BV (e_{1} e_{2}, 1 · q) = BV (e_{1}, q)
BV (e_{1} e_{2}, 2 · q) = BV (e_{2}, q)
BV ([v:t] e, 1 · q) = BV (t, q)
BV ([v:t] e, 2 · q) = v : Ty(t); BV (e, q)
BV ((v:t_{1}) t_{2}, 1 · q) = BV (t_{1}, q)
BV ((v:t_{1}) t_{2}, 2 · q) = v : Ty(t_{1}); BV (t_{2}, q)
S  BV (Ty(Ψ), 1 · q) = BV (Ψ, q)
BV (Eq(ϕ : Ψ), 1 · q) = BV (ϕ, q)
BV (Eq(ϕ : Ψ), 2 · q) = BV (Ψ, q)

m BV (Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End, i · q) =

\overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1}; BV (\overline{v}_{i}▷\overline{v}_{i} : S_{i} := P_{i}, q)

BV (Functor[v:M] m, 1 · q) = BV (M, q)
BV (Functor[v:M] m, 2 · q) = v : Ty(M); BV (m, q)
BV (p_{1} p_{2}, q) = ϵ
StrC  BV (\overline{v}▷\overline{v} : S := P, 1 · q) = BV (S, q)
BV (\overline{v}▷\overline{v} : S := P, 2 · q) = BV (P, q)
M  BV (Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End; i · q) = \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1}; BV (\overline{v}_{i}▷\overline{v}_{i} : S_{i}, q)
BV (Funsig(v:M_{1}) M_{2}, 1 · q) = BV (M_{1}; q)
BV (Funsig(v:M_{1}) M_{2}, 2 · q) = v : Ty(M); BV (M_{2}, q)
SigC  BV (\overline{v}▷\overline{v} : S, 1 · q) = BV (S, q)
B  BV (Ind(E^{I} := E^{C}), 1 · q) = BV (E^{I}, q)
BV (Ind(E^{I} := E^{C}), 2 · q) = E^{I}; BV (E^{C}, q)
BV (Rew(E^{Γ}, R), 1 · q) = BV (E^{Γ}, q)
BV (Rew(E^{Γ}, R), 2 · q) = E^{Γ}; BV (R, q)
BV (\overline{p}, q) = ϵ
BT  BV (IndSpec(E^{I} := E^{C}), 1 · q) = BV (E^{I}, q)
BV (IndSpec(E^{I} := E^{C}), 2 · q) = E^{I}; BV (E^{C}, q)
BV (RewSpec(E^{Γ}, R), 1 · q) = BV (E^{Γ}, q)
BV (RewSpec(E^{Γ}, R), 2 · q) = E^{Γ}; BV (R, q)
E  BV (\overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}, i · q) = \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1}; BV (\overline{v}_{i} : S_{i}, q)
EnvC  BV (\overline{v} : S, 1 · q) = BV (S, q)

R  BV ({E_{j} | e_{j}^{L} ⟶ e_{j}^{R} : t_{j}}_{j=1...n}, i · q) = BV (E_{i} | e_{i}^{L} ⟶ e_{i}^{R} : t_{i}, q)
Rule  BV (E | e^{L} ⟶ e^{R} : t, 1 · q) = BV (E, q)
BV (E | e^{L} ⟶ e^{R} : t, 2 · q) = E; BV (e^{L}, q)
BV (E | e^{L} ⟶ e^{R} : t, 3 · q) = E; BV (e^{R}, q)
BV (E | e^{L} ⟶ e^{R} : t, 4 · q) = E; BV (t, q)

Conservativity roadmap

We are going to show that our calculus of named modules is conservative with respect to the pure type system with inductive types and rewriting. Imposing conditions on rewriting that guarantee logical consistency of the whole typing system is a difficult task which is out of the scope of this thesis (see discussion in Chapter 11). We will therefore implicitly assume that developments in the pure type system with inductive types and rewriting are consistent.

The natural idea of reusing the conservativity proof of Courant does not work, because his proof uses the subject reduction property with respect to modules reductions, which do not exist in our calculus. It would even be impossible to define such reductions since they violate our syntactic restrictions about access paths. Nevertheless, we are going to simulate module reductions following a call-by-value strategy and show that modules can be eliminated from arbitrary developments.

3.1 Conservativity

We argue that a closed Coq development (without axioms) corresponds to a structure typable in the empty environment, where components of the structure are only manifest or abstract term definitions, for constants and lemmas respectively, and inductive definitions. Since we want to show conservativity with respect to the calculus with rewriting, we permit also definitions by rewriting. To make a difference from general structure components, we call the former ones classic.

Definition 3.1.1. A structure component is classic if it is of one of the following forms:

1. v▷v : Ty(t) := e,

2. v▷v : Eq(e : t) := e,

3. \overline{v}▷\overline{v} : Ty(IndSpec(E^{I} := E^{C})) := Ind(E^{I} := E^{C}) or

4. \overline{v}▷\overline{v} : Ty(RewSpec(E^{Γ}, R)) := Rew(E^{Γ}, R).

Classic signature components are defined similarly.

Without losing generality, we may assume that a development in Coq can be divided into three parts: modelisation, proof development and final theorem. The modelisation corresponds to writing definitions of constants (components of the form 2) and inductive definitions (of the form 3). If rewriting were already available in Coq, definitions by rewriting (of the form 4) would also participate to the modelisation phase. The proof development corresponds to writing components of any kind — these are auxiliary definitions and lemmas — and the final theorem is a component of the form 1. For example, let us imagine proving commutativity of addition on natural numbers. The modelisation part would contain the definition of natural numbers and addition, the proving phase would contain auxiliary lemmas (like the fact that zero is a left and right neutral element for addition) and the final theorem would state and prove the commutativity of addition.

Definition 3.1.2. A formal development is a structure well-typed in the empty environment, consisting of the following three parts:

• modelisation — a sequence of classic structure components \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n}

• proof development — a sequence of (any) structure components \overline{v}_{n+1}▷\overline{v}_{n+1} : S_{n+1} := P_{n+1} … \overline{v}_{m}▷\overline{v}_{m} : S_{m} := P_{m}

• final theorem — a structure component v▷v : Ty(t) := e

Moreover, the term t, called goal is well-typed in the modelisation environment, i.e.

\overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ t : ⋆

A formal development is classic if its proof development part contains only classic components.

The conservativity result to be proved in this thesis shows that no new theorems can be proved using modules and manifest blocks.

Theorem 1. Every formal development can be transformed into a classic formal development with the same modelisation and the same goal.

In other words, the fact we have to prove is the following:

Let \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} be a sequence of classic components and t a term such that \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ t : ⋆. If there exists a structure

Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} \overline{v}_{n+1}▷\overline{v}_{n+1} : S_{n+1} := P_{n+1} … \overline{v}_{m}▷\overline{v}_{m} : S_{m} := P_{m} v▷v : Ty(t) := e

well-typed is the empty environment, then there exists a classic structure of the form

End Struct

\overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n}

\overline{v}′_{n+1}▷\overline{v}′_{n+1} : S′_{n+1} := P′_{n+1} … \overline{v}′_{k}▷\overline{v}′_{k} : S′_{k} := P′_{k} v▷v : Ty(t) := e′

End

well-typed in the empty environment.

The proof of this theorem is done in two phases. First, we evaluate all module components in the initial structure from left to right, and then we eliminate modules from right to left, by simply removing functors and flattening structures. Once all functors are removed and all sub-structures are flattened, we are left with a classic structure.

In the rest of this chapter we give a more detailed presentation of the two phases.

3.2 Evaluation

In this phase, we are going to evaluate module components to a weak head normal form.

Definition 3.2.1. A module expression m is in weak head normal form if it is a path, a structure or a functor^{1}.

Definition 3.2.2. A structure component is evaluated if it is a term or block component or if it is a module component v▷v : Ty(M) := m such that

• m is in weak head normal form,

• if m is a structure then it is evaluated and M is the primary signature of m

A structure is evaluated if all its components are.

Lemma 3.2.1. Let \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} be a sequence of classic components and t a term such that \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ t : ⋆. If there exists a structure

Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} \overline{v}_{n+1}▷\overline{v}_{n+1} : S_{n+1} := P_{n+1} … \overline{v}_{m}▷\overline{v}_{m} : S_{m} := P_{m} v▷v : Ty(t) := e

End well-typed is the empty environment, then there exists an evaluated structure of the form

1but not an application

Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} \overline{v}_{n+1}▷\overline{v}_{n+1} : S′_{n+1} := P′_{n+1} … \overline{v}_{m}▷\overline{v}_{m} : S′_{m} := P′_{m} v▷v : Ty(t) := e

End

well-typed in the empty environment. The proof of this lemma is given in Chapter 6, the final result of which is Lemma 6.3.21. It works by evaluating the structure components from left to right. The evaluation operates on module components only, in the following way:

• structures are evaluated recursively and are finally given the primary signature, as follows:

v▷v : Ty(M) := Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End ⟶^{eval}^{∗}

v▷v : Ty(M) := Struct \overline{v}_{1}▷\overline{v}_{1} : Ŝ_{1} := P̂_{1} … \overline{v}_{n}▷\overline{v}_{n} : Ŝ_{n} := P̂_{n} End ⟶^{eval}

v▷v : Ty(Sig \overline{v}_{1}▷\overline{v}_{1} : Ŝ_{1} … \overline{v}_{n}▷\overline{v}_{n} : Ŝ_{n} End)

:= Struct \overline{v}_{1}▷\overline{v}_{1} : Ŝ_{1} := P̂_{1} … \overline{v}_{n}▷\overline{v}_{n} : Ŝ_{n} := P̂_{n} End

• functor applications are evaluated in the following way:

v▷v : Ty(M) := p_{1} p_{2} ⟶^{eval}

v▷v : Ty(M) := m{v̂ ↦ p_{2}} if the value of p_{1} is Functor[v̂ : M′]m and

v▷v : Ty(M) := p_{1} p_{2} ⟶^{eval}

v▷v : Ty(M) := p′ p_{2} otherwise, assuming the value of p_{1} is p′ The value of a path in the given context is defined recursively as follows:

• if the path is a variable v then the value of v is the m part of the component v▷v : Ty(M) := m occurring in the context,

• if the path is of the form p.v_{i} then:

– if the value of p is a structure

Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … v_{i}▷v_{i} : Ty(M) := m … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End

then the value of p.v_{i} is m\{\overline{v}_{j} ↦ \overline{p.v_{j}}\}.

– otherwise, assuming the value of p is p′, the value of p.v_{i} is p′.v_{i}.

The fact that the value of a path is well defined follows from the type correctness of the considered structure and from the fact that the preceding components have already been evaluated.

The evaluation process is terminating, which can be proved using the translation to the weak version of the calculus of Judicaël Courant, WMC. This calculus is used by Courant to show decidability of module typing under the simplifying assumption that typing of terms is trivial: for all terms e and t we have E ⊢_{W} e : t.

The translation of our calculus to WMC is straightforward. We translate all block and term components by a sequence of trivial components v▷v : Eq(⋆ : ⋆) := ⋆. The translation of module components is done by making all specifications manifest.

It turns out that evaluation of a path application corresponds to actual module reductions in WMC, performed on the translated expressions. Since WMC is terminating, this guarantees termination of the evaluation of a single module component.

Unfortunately module type modification performed after recursively evaluating a substructure does not correspond to a WMC reduction of the translated expression, so the termination of the whole evaluation process cannot be deduced directly. However, by carefully analyzing the normal forms of the translation to WMC before and after the module type modification, we may notice that the number of module components in both normal forms is the same. Consequently the total number of module components evaluated during the whole process is bounded and therefore the evaluation cannot go on forever.

We also prove that each evaluation step preserves the type of the whole structure. In the end, starting from a structure m of type M, we obtain an evaluated structure m′ of type M, and classic components of m are not changed in the evaluation process.

3.3 Flattening

The second phase of transforming a given structure into a classic one consists in removing the module components (the proof is given in Chapter 7 in Lemma 7.2.5):

Lemma 3.3.1. Let \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} be a sequence of classic components and t a term such that \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ t : ⋆. If there exists an evaluated structure

Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} \overline{v}_{n+1}▷\overline{v}_{n+1} : S′_{n+1} := P′_{n+1} … \overline{v}_{m}▷\overline{v}_{m} : S′_{m} := P′_{m} v : Ty(t) := e

End Struct

\overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n}

\overline{v}′_{n+1}▷\overline{v}′_{n+1} : S′_{n+1} := P′_{n+1} … \overline{v}′_{k}▷\overline{v}′_{k} : S′_{k} := P′_{k} v▷v : Ty(t) := e′

End

well-typed is the empty environment.

We proceed here from right to left, eliminating the last non-classic component in the following way:

• if the last non-classic component is a module component:

v▷v : Ty(Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End)

:= Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End

then we replace it by its sub-components, i.e:

\overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n}

and perform the inverse substitution \{\overline{v.v_{i}} ↦ \overline{v_{i}}\} on the rest of the structure

• if the last non-classic component is a module component:

v▷v : Ty(Funsig(v̂ : M_{1})M_{2}) := m

then we simply remove it

• if the last non-classic component is a module or a block component with a manifest specification:

\overline{v}▷\overline{v} : Eq(\overline{p} : Ψ) := P

or if it is implemented by a path or a sequence of paths

\overline{v}▷\overline{v} : Ty(Ψ) := \overline{p}

then we remove it and substitute \{\overline{v} ↦ \overline{p}\} on the rest of the structure.

Note that all steps decrease the total number of components in the structure and its substructures, and hence the flattening phase is terminating. The resulting structure is of course classic.

Proving the typability preservation of the flattening phase is quite difficult. Because of potential nontrivial subtypings involved in the derivation of types for term paths, the induction hypothesis of the straightforward proof would have to establish the correctness of a signature flattening in an arbitrary environment. Unfortunately the latter does not hold. Instead, we show that subtyping is not essential for term derivations and that flattening of signatures is possible in the absence of subtyping.

Technically, the above proof is done with an auxiliary typing system, called principal and denoted ⊢_{P}, which is presented and studied in Chapter 5. It consists of term rules and elimination rules for signatures, specifications and block types. We prove that all term judgments derivable in ⊢ can be derived in ⊢_{P}. In this system it becomes easy to prove the correctness of signature flattening by a straightforward induction on the derivation.

The correctness of the remaining steps (removing module and block components involving paths) follows easily from the path substitution lemma.

Note that the goal of the formal development, the term t, does not change during the flattening phase. Indeed, it does not contain names of the non-classic components of the structure and is therefore unaffected by the substitutions. Its proof e can be changed of course.

Basic metatheory

In this chapter with prove basic metatheoretical facts about our calculus of named modules. Most properties of this section are clones of the corresponding properties in [32]. Unfortunately, we had to repeat the proofs as our typing system is richer than the original one.

4.1 General properties

First we show that a correct environment assigns correct specifications to variables and that derivations are only possible in correct environments.

Lemma 4.1.1. For every environment E = \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}, every derivation of E ⊢ ok contains a derivation of \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ ok and a derivation of \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ S_{i} : spec for every i = 1 … n.

Proof. By induction on the derivation of E ⊢ ok. The case of the (Env/Empty) rule is trivial. If the last rule in the derivation is

(Env/Insert)  \frac{E′ ⊢ ok E′ ⊢ S : spec}{E′; \overline{v} : S ⊢ ok}

then the conclusions of the lemma hold for E′ by induction hypothesis. We are left to check E′ ⊢ ok and E′ ⊢ S : spec which are true as they are the premises of the rule.

Lemma 4.1.2. For every environment E and judgment J, every derivation of E ⊢ J, contains a derivation of E ⊢ ok.

Proof. By induction on the derivation. Let us look at the last rule in the derivation of E ⊢ J. If J is ok then the lemma trivially holds. In all other rules there is always at least one premise whose environment is equal to E or extends it. Hence, we can conclude by induction hypothesis (and by Lemma 4.1.1 if the environment is an extension of E).

The next lemma shows that in our typing system subterms of well-typed expressions are well-typed.

Lemma 4.1.3. For every judgment E ⊢ ϕ : Ψ, for every position q such that ϕ|_{q} is an expression, there is a type, a sort or a specification Ψ′ such that E; BV (ϕ, q) ⊢ ϕ|_{q} : Ψ′.

Proof. By induction on the derivation of E ⊢ ϕ : Ψ. Let us look at the last rule in this derivation. For most rules the lemma easily holds because all immediate subterms of ϕ appear on the left hand side of the colon symbol in one of the premises of the rule. The rule (Sig/Form) needs a bit more work:

\frac{E; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ ok}{E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End : modtype}

We first use Lemma 4.1.1 to find subderivations E; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ S_{i} : spec for every i = 1 … n and then deduce by induction hypothesis that all subterms of S_{1}, … S_{n} have types.

Lemma 4.1.4. For all environments E_{1}, E_{2} and E_{3}, if E_{1}; E_{2} ⊢ ok and E_{1}; E_{3} ⊢ J for some judgment J, then E_{1}; E_{2}; E_{3} ⊢ J.

Proof. By induction on the derivation of E_{1}; E_{3} ⊢ J. Let us look at the last rule in this derivation.

Environment In case of (Env/Empty) both E_{1} and E_{3} are empty and we need to show that E_{2} ⊢ ok which is one of the assumptions of the lemma (for E_{1} = ϵ).

For (Env/Insert)

\frac{E ⊢ ok E ⊢ S : spec}{E; \overline{v} : S ⊢ ok}

we have two cases:

1. E_{1} = E; \overline{v} : S and E_{3} = ϵ. Then E_{1}; E_{2}; E_{3} ⊢ J becomes E_{1}; E_{2} ⊢ ok which is one of the assumptions of the lemma.

2. E_{1} = E′, E_{3} = E′′; \overline{v} : S and E′; E′′ = E. By induction hypothesis applied to the premises of the rule we have E′; E_{2}; E′′ ⊢ ok and E′; E_{2}; E′′ ⊢ S : spec. Using the rule (Env/Insert) we get E′; E_{2}; E′′, \overline{v} : S ⊢ ok which is the desired conclusion.

For (Env/Lookup)

\frac{Ê_{1}; \overline{v} : S; Ê_{2} ⊢ ok}{Ê_{1}; \overline{v} : S; Ê_{2} ⊢ \overline{v} : S}

we have two cases:

1. E_{1} = Ê_{1}; \overline{v} : S; Ê_{2}′, E_{3} = Ê_{2}′′ and Ê_{2}′; Ê_{2}′′ = Ê_{2}.

By assumption we know that Ê_{1}; \overline{v} : S; Ê_{2}′; E_{2} ⊢ ok. Applying the induction hypothesis to the premise of the rule Ê_{1}; \overline{v} : S; Ê_{2}′; Ê_{2}′′ ⊢ ok, we get Ê_{1}; \overline{v} : S; Ê_{2}′; E_{2}; Ê_{2}′′ ⊢ ok and by the rule (Env/Lookup) Ê_{1}; \overline{v} : S; Ê_{2}′; E_{2}; Ê_{2}′′ ⊢ \overline{v} : S which is the desired conclusion.

2. E_{1} = Ê_{1}′, E_{3} = Ê_{1}′′; \overline{v} : S; Ê_{2} and Ê_{1}′; Ê_{1}′′ = Ê_{1}. By assumption we know that Ê_{1}′; E_{2} ⊢ ok. Applying the induction hypothesis to the premise of the rule Ê_{1}′; Ê_{1}′′; \overline{v} : S; Ê_{2} ⊢ ok, we get Ê_{1}′; E_{2}; Ê_{1}′′; \overline{v} : S; Ê_{2} ⊢ ok and by the rule (Env/Lookup) Ê_{1}′; E_{2}; Ê_{1}′′; \overline{v} : S; Ê_{2} ⊢ \overline{v} : S which is the desired conclusion.

Terms Let us consider the (Term/Prod) rule

\frac{E ⊢ t_{1} : s_{1} E; v : Ty(t_{1}) ⊢ t_{2} : s_{2}}{E ⊢ (v : t_{1})t_{2} : s_{3}} if (s_{1}, s_{2}, s_{3}) ∈ R

where E = E_{1}; E_{3}. By induction hypothesis we have E_{1}; E_{2}; E_{3} ⊢ t_{1} : s_{1} and E_{1}; E_{2}; E_{3}; v : Ty(t_{1}) ⊢ t_{2} : s_{2}. Using the (Term/Prod) rule we get E_{1}; E_{2}; E_{3} ⊢ (v : t_{1})t_{2} : s_{3}.

Other rules for terms are dealt with similarly.

Modules, Specifications In all rules, the environments of the premises are extensions of the environment of the conclusion. Hence, the lemma holds by induction hypothesis (as above).

Blocks For block rules the situation is similar to term and module rules, but there are additional side conditions in the rules (IndSpec/Form), (RewSpec/Form) and (RewSpec/Rew). Fortunately, these side conditions are satisfied in the extended environment by the closure property (C2).

Another rule to be checked is (RewSpec/Comp):

\frac{E ⊢ \overline{p} : RewSpec(E^{Γ}, R) δ : Ê_{i} → E}{E ⊢ e_{i}^{L}θδ ≈ e_{i}^{R}θδ} where θ = \{v_{j} ↦ p^{j}\}_{j=1...n}

where E = E_{1}; E_{3} and E^{Γ} = v_{1} : T y(t_{1}) … v_{n} : T y(t_{n}). By induction hypothesis we have E_{1}; E_{2}; E_{3} ⊢ \overline{p} : RewSpec(E^{Γ}, R). In order to apply (RewSpec/Comp) and to get E_{1}; E_{2}; E_{3} ⊢ e_{i}^{L}θδ ≈ e_{i}^{R}θδ, we have to make sure that δ : Ê_{i} → E_{1}; E_{2}; E_{3}. But, we have δ : Ê_{i} → E which means that E ⊢ vδ : tδ for all v : t ∈ Ê_{i}. By induction hypothesis, we have also E_{1}; E_{2}; E_{3} ⊢ vδ : tδ for all v : t ∈ Ê_{i}, i.e. δ : Ê_{i} → E_{1}; E_{2}; E_{3}.

Convertibility Like for terms, modules and specifications.

4.1.1 Substitutivity of paths

Lemma 4.1.5. For all environments E_{1} and E_{2}, judgment J, specification S and a sequence of paths \overline{p}, if E_{1}; \overline{v} : S; E_{2} ⊢ J and E_{1} ⊢ \overline{p} : S then E_{1}; E_{2}\{\overline{v} ↦ \overline{p}\} ⊢ J\{\overline{v} ↦ \overline{p}\}.

Proof. By induction on the derivation of E_{1}; \overline{v} : S; E_{2} ⊢ J. We inspect the last rule in this derivation. Let us denote the substitution \{\overline{v} ↦ \overline{p}\} by δ.

Environment The rule (Env/Empty) cannot be the last rule of the derivation.

For (Env/Insert):

\frac{E ⊢ ok E ⊢ S′ : spec}{E; \overline{v}′ : S′ ⊢ ok}

we distinguish two cases:

1. \overline{v}′ : S′ = \overline{v} : S, E_{1} = E and E_{2} = ϵ. We have to show that E ⊢ ok, which is a premise of the rule.

2. E_{2} = E′, \overline{v}′ : S′, and E = E_{1}; \overline{v} : S; E′. We want to show that E_{1}; E′δ; \overline{v}′ : S′δ ⊢ ok. By induction hypothesis applied to the premises of the rule we get E_{1}; E′δ ⊢ ok and E_{1}; E′δ ⊢ S′δ : spec. Using (Env/Insert) we get the desired conclusion.

For (Env/Lookup)

\frac{Ê_{1}; \overline{v}′ : S′; Ê_{2} ⊢ ok}{Ê_{1}; \overline{v}′ : S′; Ê_{2} ⊢ \overline{v}′ : S′}

we have three cases:

1. \overline{v}′ : S′ ∈ E_{1} and the conclusion of the rule is Ê_{1}; \overline{v}′ : S′; E′_{1}; \overline{v} : S; E_{2} ⊢ \overline{v}′ : S′. We want to show that Ê_{1}; \overline{v}′ : S′, E′_{1}; E_{2}δ ⊢ \overline{v}′δ : S′δ. By induction hypothesis applied to Ê_{1}; \overline{v}′ : S′; E′_{1}; \overline{v} : S; E_{2} ⊢ ok we have Ê_{1}; \overline{v}′ : S′, E′_{1}; E_{2}δ ⊢ ok. Using (Env/Lookup) we get Ê_{1}; \overline{v}′ : S′, E′_{1}; E_{2}δ ⊢ \overline{v}′ : S′ which is the desired conclusion since \overline{v} do not occur in S′ and S′δ = S′.

2. \overline{v}′ : S′ = \overline{v} : S and the conclusion of the rule is E_{1}; \overline{v} : S; E_{2} ⊢ \overline{v} : S. We want to show that E_{1}; E_{2}δ ⊢ \overline{v}δ : Sδ, i.e. E_{1}; E_{2}δ ⊢ \overline{p} : S. By assumption we know that E_{1} ⊢ \overline{p} : S. Since E_{1}; E_{2}δ ⊢ ok (by induction hypothesis applied to the premise of the rule) we can use Lemma 4.1.4 to get E_{1}; E_{2}δ ⊢ \overline{p} : S.

3. \overline{v}′ : S′ ∈ E_{2} and the conclusion of the rule is E_{1}; \overline{v} : S; E′_{2}; \overline{v}′ : S′; Ê_{2} ⊢ \overline{v}′ : S′. We want to show that E_{1}; E′_{2}δ; \overline{v}′ : S′δ; E_{2}δ ⊢ \overline{v}′ : S′δ. By induction hypothesis applied to the premise of the rule we have E_{1}; E′_{2}δ; \overline{v}′ : S′δ; E_{2}δ ⊢ ok and using (Env/Lookup) we get the desired conclusion.

Terms Let us do the proof for the (Term/App) rule. Other cases are similar.

\frac{E ⊢ e : (v′ : t_{1})t_{2} E ⊢ e′ : t_{1}}{E ⊢ e e′ : t_{2}\{v′ ↦ e′\}}

where E = E_{1}; \overline{v} : S; E_{2}. By induction hypothesis we have E_{1}; E_{2}δ ⊢ eδ : ((v′ : t_{1})t_{2})δ and E_{1}; E_{2}δ ⊢ e′δ : t_{1}δ. Using the (Term/App) rule we get E_{1}; E_{2}δ ⊢ (eδ)(e′δ) :

(t_{2}δ){v′ ↦ e′δ}, which is the desired conclusion since (t_{2}δ){v′ ↦ e′δ} = (t_{2}{v′ ↦ e′\})δ (because v′ ∉ \overline{p}).

Modules The proofs work by a simple use of the induction hypothesis. We also use the fact (in rules (Sig/Access) and (Mod/App)) that p′δ is a path.

The only nontrivial case is (Strengthenning) where we have to know that (M/p′)δ = Mδ/p′δ which can be shown by easy induction on the structure of M.

Specifications The proofs are like those for terms, but simpler.

Blocks For blocks rules the situation is similar to term and module rules, but additionally there are some side conditions in (IndSpec/Form), (RewSpec/Form) and (RewSpec/Rew). Fortunately they are satisfied for the substituted environment by the closure property (C1).

Another rule where the proof is not straightforward is (RewSpec/Comp):

\frac{E ⊢ \overline{p}′ : RewSpec(E^{Γ}, R) ξ : Ê_{i} → E}{E ⊢ e_{i}^{L}θξ ≈ e_{i}^{R}θξ} where θ = \{v_{j} ↦ p′_{j}\}_{j=1...n}

where E = E_{1}; \overline{v} : S; E_{2}. By induction hypothesis E_{1}; E_{2}δ ⊢ \overline{p}′δ : RewSpec(E^{Γ}δ, Rδ). Since ξ : Ê_{i} → E means that E ⊢ v̂ξ : tξ for all v̂ : t ∈ Ê_{i}, by induction hypothesis we have E_{1}; E_{2}δ ⊢ v̂ξδ : tξδ for all v̂ : t ∈ Ê_{i} and consequently ξδ : Ê_{i} → E_{1}; E_{2}δ. Let us also take θδ = {v_{j} ↦ p′_{j}δ}_{j=1...n} instead of θ. Using (RewSpec/Comp) we get E_{1}; E_{2}δ ⊢ e_{i}^{L}δ(θδ)(ξδ) ≈ e_{i}^{R}δ(θδ)(ξδ). It is equivalent to the desired conclusion (E_{1}; E_{2}δ ⊢ (e_{i}^{L}δθξ)δ ≈ (e_{i}^{R}δθξ)δ) because the domains of θ and ξ are disjoint with the codomain of δ.

Convertibility The proofs are like for term and module rules. The only nontrivial case is (Conv/Refl):

\frac{E_{1}; \overline{v} : S; E_{2} ⊢ v′ : M}{E_{1}; \overline{v} : S; E_{2} ⊢ v′ ≈ v′}

If v′ ∉ \overline{v} then we have E_{1}; E_{2}δ ⊢ v′ : Mδ by induction hypothesis and E_{1}; E_{2}δ ⊢ v′ ≈ v′ by (Conv/Refl).

If v′ ∈ \overline{v} then S is a module specification and length(\overline{v}) = length(\overline{p}) = 1. We have to show that E_{1}; E_{2}δ ⊢ p ≈ p. Recall that E_{1} ⊢ p : S by assumption of the lemma. Let us prove that E_{1} ⊢ p ≈ p by induction on the structure of p = v′′.v_{1} ….v_{n}.

If n = 0 then E_{1} ⊢ v′′ ≈ v′′ follows from (Conv/Refl). Suppose now that p = p′.v_{n+1}. By Lemma 4.1.3 we have E_{1} ⊢ p′ : M′ for some M′ and by induction hypothesis E_{1} ⊢ p′ ≈ p′. Now, it is sufficient to use (Conv/Path) to get E_{1} ⊢ p ≈ p.

Going back to the main proof, we can now use Lemma 4.1.4 on E_{1} ⊢ p ≈ p to get E_{1}; E_{2}δ ⊢ p ≈ p which is the desired conclusion.

Lemma 4.1.6. Let E_{1}, E_{2}, E be environments, J a judgment, and δ a substitution assigning paths to variables. If E_{1}; E_{2} ⊢ J, E ⊢ ok and E ⊢ \overline{v}δ : Sδ for every \overline{v} : S ∈ E_{1} then E; E_{2}δ ⊢ Jδ.

Proof. Let E_{1} = \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}. By Lemma 4.1.4 we have E; E_{1}; E_{2} ⊢ J. Now, it is sufficient to eliminate variables from E_{1} from left to right, using Lemma 4.1.5 with a substitution which assigns a sequence of paths \overline{v}_{i}δ to every \overline{v}_{i}.

The following lemma is very useful throughout all metatheoretical studies. It says that variables in the environment can be replaced by others, provided the new ones satisfy the original specifications.

Lemma 4.1.7. Let E_{1}, E_{2} be environments and S_{1} … S_{k} and S′_{1} … S′_{m} specifications. Suppose that

E_{1}; \overline{v}_{1} : S_{1} … \overline{v}_{k} : S_{k} ⊢ ok and E_{1}; \overline{v}′_{1} : S′_{1} … \overline{v}′_{m} : S′_{m}; E_{2} ⊢ J.

If there exists a substitution θ : \{\overline{v}′_{1} … \overline{v}′_{m}\} → \{\overline{v}_{1} … \overline{v}_{k}\} such that for all i = 1 … m E_{1}; \overline{v}_{1} : S_{1} … \overline{v}_{k} : S_{k} ⊢ \overline{v}′_{i}θ : S′_{i}θ then E_{1}; \overline{v}_{1} : S_{1} … \overline{v}_{k} : S_{k}; E_{2}θ ⊢ Jθ.

Proof. It is a corollary from Lemma 4.1.6. Let E_{v} = \overline{v}_{1} : S_{1} … \overline{v}_{k} : S_{k}, E′_{v} = \overline{v}′_{1} : S′_{1} … \overline{v}′_{m} : S′_{m}, E = E_{1}; E_{v} and E′_{1} = E_{1}; E′_{v}. By assumptions of the lemma we have E′_{1}; E_{2} ⊢ J, E ⊢ ok and by extending θ to be the identity on the domain of E_{1}, we get E ⊢ \overline{v}θ : Sθ for every vv : S ∈ E′_{1}. Of course θ is a path substitution and we can use Lemma 4.1.6 to get E; E_{2}θ ⊢ Jθ which is E_{1}; \overline{v}_{1} : S_{1} … \overline{v}_{k} : S_{k}; E_{2}θ ⊢ Jθ.

4.1.2 Module types, subtyping and convertibility

In this subsection we show that our system has expected properties with respect to module types. First, we show that a correct signature contains correct specifications. Then we prove several lemmas about subtyping, convertibility and their relationship. We end with Lemma 4.1.15 stating, among others, that if a module expression has a module type then the latter is correct.

Lemma 4.1.8. In every derivation of E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End : modtype there is a derivation of E; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ ok and a derivation of E; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ S_{i} : spec for all i = 1 … n.

Proof. The (Sig/Form) rule is the only one that can be used to derive the judgment E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End : modtype. Its premises give the first conclusion of our lemma. The second follows from Lemma 4.1.1.

Lemma 4.1.9. For every environment E and module types M_{1}, M_{2} and M_{3}, if E ⊢ M_{1} <: M_{2} and E ⊢ M_{2} <: M_{3} then E ⊢ M_{1} <: M_{3}.

Proof. It is easy to see that M_{1}, M_{2} and M_{3} are either all functor types or all signatures. We obtain the result by structural induction on the concerned module types.

1. Let M_{1} = Funsig(v : M′_{1})M′′_{1}, M_{2} = Funsig(v : M′_{2})M′′_{2} and M_{3} = Funsig(v : M′_{3})M′′_{3}. The last rules in the derivations of E ⊢ M_{1} <: M_{2} and E ⊢ M_{2} <: M_{3} have to be (Funsig/Sub). The premises of these rules give us: E ⊢ M′_{2} <: M′_{1}, E, v : Ty(M′_{2}) ⊢ M′′_{1} <: M′′_{2} and E ⊢ M′_{3} <: M′_{2}, E, v : Ty(M′_{3}) ⊢ M′′_{2} <: M′′_{3}. By induction hypothesis we have E ⊢ M′_{3} <: M′_{1}. If we manage to prove E, v : Ty(M′_{3}) ⊢ M′′_{1} <: M′′_{3}, we can apply (Funsig/Sub) to get E ⊢ Funsig(v : M′_{1})M′′_{1} <: Funsig(v : M′_{3})M′′_{3}.

In order to get E, v : Ty(M′_{3}) ⊢ M′′_{1} <: M′′_{3} we apply induction hypothesis to E, v : Ty(M′_{3}) ⊢ M′′_{2} <: M′′_{3} and E, v : Ty(M′_{3}) ⊢ M′′_{1} <: M′′_{2} that we can prove by Lemma 4.1.7. Note that we have E; v : Ty(M′_{3}) ⊢ ok by Lemma 4.1.2 and E, v : Ty(M′_{2}) ⊢ M′′_{1} <: M′′_{2} is one of the premises listed above. If we had E, v : Ty(M′_{3}) ⊢ v : Ty(M′_{2}) we could use Lemma 4.1.7 and get E, v : Ty(M′_{3}) ⊢ M′′_{1} <: M′′_{3}.

We are left to show E, v : Ty(M′_{3}) ⊢ v : Ty(M′_{2}). Starting from E; v : Ty(M′_{3}) ⊢ v : Ty(M′_{3}), which is true as E, v : Ty(M′_{3}) ⊢ ok, we can get E, v : Ty(M′_{3}) ⊢ v : M′_{3} by (Ty/Type). Then we apply (Subsumption) with E, v : Ty(M′_{3}) ⊢ M′_{3} <: M′_{2} (which is true since we have E ⊢ M′_{3} <: M′_{2} and E, v : Ty(M′_{3}) ⊢ ok) and get E, v : Ty(M′_{3}) ⊢ v : M′_{2}. Finally, by (Ty/Sat), we obtain E, v : Ty(M′_{3}) ⊢ v : Ty(M′_{2}). The use of (Ty/Sat) is correct because we have E, v : Ty(M′_{2}) ⊢ ok and by Lemma 4.1.1 E ⊢ Ty(M′_{2}) : spec.

2. Let M_{1} = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End, M_{2} = Sig \overline{v}′_{1}▷\overline{v}′_{1} : S′_{1} … \overline{v}′_{n′}▷\overline{v}′_{n′} : S′_{n′} End and M_{3} = Sig \overline{v}′′_{1}▷\overline{v}′′_{1} : S′′_{1} … \overline{v}′′_{n′′}▷\overline{v}′′_{n′′} : S′′_{n′′} End. The last rules in the derivations of E ⊢ M_{1} <: M_{2} and E ⊢ M_{2} <: M_{3} have to be (Sig/Sub). The premises of these rules give us: E; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ \overline{v}′_{k} : S′_{k} for k = 1 … n′ and E; \overline{v}′_{1} : S′_{1} … \overline{v}′_{n} : S′_{n} ⊢ \overline{v}′′_{k} : S′′_{k} for k = 1 … n′′. We want to show that E; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ \overline{v}′′_{k} : S′′_{k} for k = 1 … n′′.

To this end for every k = 1 … n′′ we can use Lemma 4.1.7. We have E; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ ok, E; \overline{v}′_{1} : S′_{1} … \overline{v}′_{n′} : S′_{n′} ⊢ \overline{v}′′_{k} : S′′_{k} and E; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ \overline{v}′_{i} : S′_{i} for i = 1 … n′. Consequently, E; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ \overline{v}′′_{k} : S′′_{k}.

The following lemma shows that convertible module types are subtypes of each other. Of course we have to consider specifications in the same time.

Lemma 4.1.10. For every environment E, module types M_{1}, M_{2} and specifications S_{1}, S_{2}:

• if E ⊢ M_{1} ≈ M_{2} then E ⊢ M_{1} <: M_{2} and E ⊢ M_{2} <: M_{1}

• if E ⊢ S_{1} ≈ S_{2} then E; \overline{v} : S_{1} ⊢ \overline{v} : S_{2} and E; \overline{v} : S_{2} ⊢ \overline{v} : S_{1}.

Proof. By induction on the derivation of E ⊢ M_{1} ≈ M_{2} and E ⊢ S_{1} ≈ S_{2}.

1. Let E ⊢ M_{1} ≈ M_{2}. There are two possibilities for M_{1} and M_{2}. Either they are both functor types or signatures:

(a) M_{1} = Funsig(v : M′_{1})M′′_{1}, M_{2} = Funsig(v : M′_{2})M′′_{2}. Since E ⊢ M_{1} ≈ M_{2} can be obtained only by (Conv/Funsig) we know that E ⊢ Funsig(v : M′_{1})M′′_{1} : modtype, E ⊢ M′_{1} ≈ M′_{2}, E; v : Ty(M′_{1}) ⊢ M′′_{1} ≈ M′′_{2} and E ⊢ Funsig(v : M′_{2})M′′_{2} : modtype. By induction hypothesis we get

E ⊢ M′_{1} <: M′_{2}  (4.1)
E ⊢ M′_{2} <: M′_{1}  (4.2)
E; v : Ty(M′_{1}) ⊢ M′′_{1} <: M′′_{2}  (4.3)
E; v : Ty(M′_{1}) ⊢ M′′_{2} <: M′′_{1}  (4.4)

From E; v : Ty(M′_{1}) ⊢ M′′_{1} <: M′′_{2} and E; v : Ty(M′_{2}) ⊢ v : Ty(M′_{1}) (which is true by induction hypothesis) we get E; v : Ty(M′_{2}) ⊢ M′′_{1} <: M′′_{2} by Lemma 4.1.7. Applying (Funsig/Sub) to (4.1) and (4.4) we get E ⊢ Funsig(v : M′_{2})M′′_{2} <: Funsig(v : M′_{1})M′′_{1} and applying (Funsig/Sub) to (4.2) and E; v : Ty(M′_{2}) ⊢ M′′_{1} <: M′′_{2} we get E ⊢ Funsig(v : M′_{1})M′′_{1} <: Funsig(v : M′_{2})M′′_{2}.

(b) M_{1} = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End, M_{2} = Sig \overline{v}_{1}▷\overline{v}_{1} : S′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S′_{n} End. Since E ⊢ M_{1} ≈ M_{2} can be obtained only by (Conv/Sig) we know that E ⊢ M_{1} : modtype, E ⊢ M_{2} : modtype and E; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ S_{i} ≈ S′_{i} for i = 1 … n. By induction hypothesis E; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1}; \overline{v}_{i} : S_{i} ⊢ \overline{v}_{i} : S′_{i} for i = 1 … n. Since E; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ ok we can use Lemma 4.1.4 to get E; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ \overline{v}_{i} : S′_{i} for i = 1 … n. By (Sig/Sub) we obtain E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End <: Sig \overline{v}_{1}▷\overline{v}_{1} : S′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S′_{n} End. To get E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S′_{n} End <: Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End we need judgments E; \overline{v}_{1} : S′_{1} … \overline{v}_{i−1} : S′_{i−1}; \overline{v}_{i} : S′_{i} ⊢ \overline{v}_{i} : S_{i} for i = 1 … n. From E; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ S_{i} ≈ S′_{i} for i = 1 … n we get by induction hypothesis E; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1}; \overline{v}_{i} : S′_{i} ⊢ \overline{v}_{i} : S_{i} for i = 1 … n. For i = 1 we have E; \overline{v}_{1} : S′_{1} ⊢ \overline{v}_{1} : S_{1}. For i=2 we have E, \overline{v}_{1} : S_{1}, \overline{v}_{2} : S′_{2} ⊢ \overline{v}_{2} : S_{2} and since E; \overline{v}_{1} : S′_{1}, \overline{v}_{2} : S′_{2} ⊢ \overline{v}_{1} : S_{1} (it follows from E; \overline{v}_{1} : S′_{1} ⊢ \overline{v}_{1} : S_{1} by Lemma 4.1.4) we can use Lemma 4.1.7 and get E, \overline{v}_{1} : S′_{1}, \overline{v}_{2} : S′_{2} ⊢ \overline{v}_{2} : S_{2}. We continue like this till i = n and then apply (Sig/Sub).

2. Let E ⊢ S_{1} ≈ S_{2}. There are two possibilities for S_{1} and S_{2}, they are both manifest or abstract specifications. Let us do the proof for manifest ones: S_{1} = Eq(ϕ_{1} : Ψ_{1}), S_{2} = Eq(ϕ_{2} : Ψ_{2}). Since E ⊢ S_{1} ≈ S_{2} can be obtained only by (Conv/Eq) we know that E ⊢ S_{1} : spec, E ⊢ ϕ_{1} ≈ ϕ_{2}, E ⊢ Ψ_{1} ≈ Ψ_{2}, E ⊢ S_{2} : spec. There are still three possibilities: this can be a module, a term or a block specification.

(a) Module specification, S_{1} = Eq(p_{1} : M_{1}), S_{2} = Eq(p_{2} : M_{2}). By induction hypothesis applied to E ⊢ M_{1} ≈ M_{2} we get E ⊢ M_{1} <: M_{2} and E ⊢ M_{2} <: M_{1}. Now it is easy to get the desired conclusion. Starting from E; v : S_{1} ⊢ \overline{v} : S_{1}, by (Eq/Type) we get E; v : S_{1} ⊢ v : M_{1} and by (Subsumption) we get E; v : S_{1} ⊢ v : M_{2}. Applying (Eq/Comp) to E; v : S_{1} ⊢ v : S_{1} gives E; v : S_{1} ⊢ v ≈ p_{1} and since E; v : S_{1} ⊢ p_{1} ≈ p_{2} by Lemma 4.1.4, we get E; v : S_{1} ⊢ v ≈ p_{2}.

Now, by (Eq/Sat) we get E; v : S_{1} ⊢ v : Eq(p_{2} : M_{2}), i.e. E; v : S_{1} ⊢ v : S_{2}. Similarily using (Conv/Symm), we can prove E; v : S_{2} ⊢ v : S_{1}.

(b) Term or block specification. In this case we use conversion (Term/Conv) or (Block/Conv) instead of (Subsumption). Starting from E; \overline{v} : S_{1} ⊢ \overline{v} : S_{1}, by (Eq/Type) we get E; \overline{v} : S_{1} ⊢ \overline{v} : Ψ_{1}, by conversion we get E; \overline{v} : S_{1} ⊢ \overline{v} : Ψ_{2}, and by (Eq/Sat) we finally obtain E; \overline{v} : S_{1} ⊢ \overline{v} : Eq(ϕ_{2} : Ψ_{2}).

Lemma 4.1.11. For every environment E and specifications S_{1}, S_{2}, if E ⊢ ϕ : S_{1} and E ⊢ S_{1} ≈ S_{2} then E ⊢ ϕ : S_{2}.

Proof. The judgment E ⊢ S_{1} ≈ S_{2} was either proved by (Conv/Ty) or (Conv/Eq). By inverting these rules we get:

1. S_{1} = Ty(Ψ_{1}), S_{2} = Ty(Ψ_{2}), E ⊢ Ψ_{1} ≈ Ψ_{2}, E ⊢ Ty(Ψ_{1}) : spec and E ⊢ Ty(Ψ_{2}) : spec.

(a) if Ψ_{1}, Ψ_{2} are terms or block types then E ⊢ ϕ : Ty(Ψ_{1}) implies E ⊢ ϕ : Ψ_{1} and E ⊢ Ty(Ψ_{2}) : spec implies E ⊢ Ψ_{2} : s, if Ψ_{2} is a term, and E ⊢ Ψ_{2} : blocktype otherwise. By (Term/Conv) (or (Block/Conv)) we get E ⊢ ϕ : Ψ_{2} and by (Ty/Sat) E ⊢ ϕ : Ty(Ψ_{2}).

(b) if Ψ_{1}, Ψ_{2} are module types then E ⊢ ϕ : Ty(Ψ_{1}) implies E ⊢ ϕ : Ψ_{1}. By Lemma 4.1.10 we have E ⊢ Ψ_{1} <: Ψ_{2} and by (Subsumption) E ⊢ ϕ : Ψ_{2}. Finally, by (Ty/Sat), we get E ⊢ ϕ : Ty(Ψ_{2}).

2. S_{1} = Eq(ϕ_{1} : Ψ_{1}), S_{2} = Ty(ϕ_{2} : Φ_{2}), E ⊢ ϕ_{1} ≈ ϕ_{2}, E ⊢ Ψ_{1} ≈ Ψ_{2}, E ⊢ Eq(ϕ_{1} : Ψ_{1}) : spec and E ⊢ Eq(ϕ_{2} : Ψ_{2}) : spec.

(a) if Ψ_{1}, Ψ_{2} are terms or block types then E ⊢ ϕ : Eq(ϕ_{1} : Ψ_{1}) implies E ⊢ ϕ : Ψ_{1} and E ⊢ Eq(ϕ_{2} : Ψ_{2}) : spec implies E ⊢ Ψ_{2} : s if Ψ_{2} is a term, and E ⊢ Ψ_{2} : blocktype otherwise. By (Term/Conv) (or (Block/Conv)) we get E ⊢ ϕ : Ψ_{2}. Moreover, we have E ⊢ ϕ ≈ ϕ_{1} by (Eq/Comp) applied to E ⊢ ϕ : Eq(ϕ_{1} : Ψ_{1}), E ⊢ ϕ_{1} ≈ ϕ_{2} by assumption and E ⊢ ϕ ≈ ϕ_{2} by (Conv/Trans). By (Eq/Sat) we get the desired conclusion E ⊢ ϕ : Eq(ϕ_{2} : Ψ_{2}).

(b) if Ψ_{1}, Ψ_{2} are module types then E ⊢ ϕ : Eq(ϕ_{1} : Ψ_{1}) implies E ⊢ ϕ : Ψ_{1}. By Lemma 4.1.10 we have E ⊢ Ψ_{1} <: Ψ_{2} and by (Subsumption) E ⊢ ϕ : Ψ_{2}. Like above we prove that E ⊢ ϕ ≈ ϕ_{2} and finally, by (Eq/Sat), we get E ⊢ ϕ : Eq(ϕ_{2} : Ψ_{2}).

Lemma 4.1.12. For every environment E and Ψ_{1}, Ψ_{2}, Ψ_{3} that are all module types, specifications or block types

• if E ⊢ Ψ : Ξ then E ⊢ Ψ ≈ Ψ (reflexivity)

• if E ⊢ Ψ_{1} ≈ Ψ_{2} then E ⊢ Ψ_{2} ≈ Ψ_{1} (symmetry)

• if E ⊢ Ψ_{1} ≈ Ψ_{2} and E ⊢ Ψ_{2} ≈ Ψ_{3} then E ⊢ Ψ_{1} ≈ Ψ_{3} (transitivity).

Proof. Let us do the proofs first for module types, then for specifications and finally for block types. The proofs are done by structural induction.

1. Module types

Reflexivity There are two cases: functor types and signatures.

(a) If M = Funsig(v : M′)M′′ then E ⊢ M′ : modtype and E; v : Ty(M′) ⊢ M′′ : modtype, since E ⊢ M : modtype can be obtained only by (Funsig/Form). By induction hypothesis we have E ⊢ M′ ≈ M′ and E; v : Ty(M′) ⊢ M′′ ≈ M′′. By (Conv/Sig) E ⊢ Funsig(v : M′)M′′ ≈ Funsig(v : M′)M′′.

(b) If M = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End and E ⊢ M : modtype then E; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ S_{i} : spec for every i = 1 … n. By induction hypothesis we have E; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ S_{i} ≈ S_{i} for every i = 1 … n and by (Conv/Sig) we get the desired conclusion E ⊢ M ≈ M.

Symmetry There two cases: functor types and signatures.

(a) If M_{1} = Funsig(v : M′_{1})M′′_{1} then M_{2} = Funsig(v : M′_{2})M′′_{2} for some M′_{2}, M′′_{2} and since their equality can be shown only by (Conv/Funsig), we know that E ⊢ M′_{1} ≈ M′_{2} and E; v : Ty(M′_{1}) ⊢ M′′_{1} ≈ M′′_{2}. By induction hypothesis we have E ⊢ M′_{2} ≈ M′_{1}. By Lemma 4.1.10 we have E; v : Ty(M′_{2}) ⊢ v : Ty(M′_{1}) and by Lemma 4.1.7 applied to E; v : Ty(M′_{1}) ⊢ M′′_{1} ≈ M′′_{2} we get E; v : Ty(M′_{2}) ⊢ M′′_{1} ≈ M′′_{2}. By induction hypothesis we have E; v : Ty(M′_{2}) ⊢ M′′_{2} ≈ M′′_{1} and by (Conv/Funsig) E ⊢ Funsig(v : M′_{2})M′′_{2} ≈ Funsig(v : M′_{1})M′′_{1}.

(b) If M_{1} = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End and E ⊢ M_{1} ≈ M_{2} then M_{2} = Sig \overline{v}_{1}▷\overline{v}_{1} : S′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S′_{n} End for some S′_{1}, …, S′_{n}. Since E ⊢ M_{1} ≈ M_{2} we know that E ⊢ M_{1} : modtype, E ⊢ M_{2} : modtype and E; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ S_{i} ≈ S′_{i} for every i = 1 … n. Let us show that E; \overline{v}_{1} : S′_{1} … \overline{v}_{i−1} : S′_{i−1} ⊢ S_{i} ≈ S′_{i} for every i = 1 … n. We have E ⊢ S_{1} ≈ S′_{1} and by induction hypothesis E ⊢ S′_{1} ≈ S_{1}. By Lemma 4.1.11 E; \overline{v}_{1} : S′_{1} ⊢ \overline{v}_{1} : S_{1}. We also have E; \overline{v}_{1} : S_{1} ⊢ S_{2} ≈ S′_{2}. If we apply Lemma 4.1.7 then we get E; \overline{v}_{1} : S′_{1} ⊢ S_{2} ≈ S′_{2} and by induction hypothesis E; \overline{v}_{1} : S′_{1} ⊢ S′_{2} ≈ S_{2}. Similarly, we can convert E; \overline{v}_{1} : S_{1}; \overline{v}_{2} : S_{2} ⊢ S_{3} ≈ S′_{3} into E; \overline{v}_{1} : S′_{1}; \overline{v}_{2} : S′_{2} ⊢ S_{3} ≈ S′_{3} (by Lemma 4.1.7, E; \overline{v}_{1} : S′_{1}; \overline{v}_{2} : S′_{2} ⊢ \overline{v}_{2} : S_{2}, which is true by Lemma 4.1.11, and E; \overline{v}_{1} : S′_{1}; \overline{v}_{2} : S′_{2} ⊢ \overline{v}_{1} : S_{1}, which is true by Lemma 4.1.4). Then, by induction hypothesis, we get E; \overline{v}_{1} : S′_{1}; \overline{v}_{2} : S′_{2} ⊢ S′_{3} ≈ S_{3}. After continuing like that to i = n we can apply (Conv/Sig) and obtain E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S′_{n} End ≈ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End.

Transitivity Like above there are two cases: functor types and signatures.

(a) Let M_{1} = Funsig(v : M′_{1})M′′_{1}, M_{2} = Funsig(v : M′_{2})M′′_{2} and M_{3} = Funsig(v : M′_{3})M′′_{3}. From E ⊢ M_{1} ≈ M_{2} and E ⊢ M_{2} ≈ M_{3} we know that E ⊢ M′_{1} ≈ M′_{2}, E ⊢ M′_{2} ≈ M′_{3}, E; v : Ty(M′_{1}) ⊢ M′′_{1} ≈ M′′_{2} and E; v : Ty(M′_{2}) ⊢ M′′_{2} ≈ M′′_{3}. By induction hypothesis we have E ⊢ M′_{1} ≈ M′_{3}. Like in the symmetry case, by Lemmas 4.1.7 and 4.1.10 we get E; v : Ty(M′_{1}) ⊢ M′′_{2} ≈ M′′_{3} and by induction hypothesis E; v : Ty(M′_{1}) ⊢ M′′_{1} ≈ M′′_{3}. Finally, by (Conv/Funsig) it follows that E ⊢ Funsig(v : M′_{1})M′′_{1} ≈ Funsig(v : M′_{3})M′′_{3}.

(b) Let M_{1} = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End, M_{2} = Sig \overline{v}_{1}▷\overline{v}_{1} : S′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S′_{n} End and M_{3} = Sig \overline{v}_{1}▷\overline{v}_{1} : S′′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S′′_{n} End. From E ⊢ M_{1} ≈ M_{2} and E ⊢ M_{2} ≈ M_{3} we know that E; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ S_{i} ≈ S′_{i} and E; \overline{v}_{1} : S′_{1} … \overline{v}_{i−1} : S′_{i−1} ⊢ S′_{i} ≈ S′′_{i} for every i = 1 … n. Like in the symmetry case we can convert E; \overline{v}_{1} : S′_{1} … \overline{v}_{i−1} : S′_{i−1} ⊢ S′_{i} ≈ S′′_{i} into E; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ S′_{i} ≈ S′′_{i} for i = 1 … n. Then by induction hypothesis we have E; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ S_{i} ≈ S′′_{i} and by (Conv/Sig) E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End ≈ Sig \overline{v}_{1}▷\overline{v}_{1} : S′′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S′′_{n} End.

2. Specifications

Reflexivity Let us do the proof for manifest specifications, for abstract ones the proof is similar. Let S = Eq(ϕ : Ψ). Since E ⊢ S : spec can be obtained only by (Eq/Form) we know that E ⊢ ϕ : Ψ and E ⊢ Ψ : Ξ. By induction hypothesis E ⊢ Ψ ≈ Ψ. The expression ϕ is a term, a path or a sequence of paths. If it is a term then E ⊢ ϕ ≈ ϕ by (Conv/Beta). If it is a path p = v.v_{1} … v_{n} then we have E ⊢ v ≈ v from reflexivity for module variables. After n applications of (Conv/Path) we get E ⊢ p ≈ p. For sequences of paths the reasonning is similar. Summarizing, we have E ⊢ Ψ ≈ Ψ and E ⊢ ϕ ≈ ϕ. Hence, by (Conv/Eq), we get E ⊢ Eq(ϕ : Ψ) ≈ Eq(ϕ : Ψ).

Symmetry Let us do the proof for manifest specifications, for abstract ones the proof is similar. Let S_{1} = Eq(ϕ_{1} : Ψ_{1}), S_{2} = Eq(ϕ_{2} : Ψ_{2}). Since E ⊢ S_{1} ≈ S_{2} can result only from the (Conv/Eq) rule, we know that E ⊢ S_{1} : spec, E ⊢ S_{2} : spec, E ⊢ ϕ_{1} ≈ ϕ_{2} and E ⊢ Ψ_{1} ≈ Ψ_{2}. By induction hypothesis E ⊢ ϕ_{2} ≈ ϕ_{1} and E ⊢ Ψ_{2} ≈ Ψ_{1} and by (Conv/Eq) E ⊢ Eq(ϕ_{2} : Ψ_{2}) ≈ Eq(ϕ_{1} : Ψ_{1}).

Transitivity Like above let us do the proof only for manifest specifications S_{1} = Eq(ϕ_{1} : Ψ_{1}), S_{2} = Eq(ϕ_{2} : Ψ_{2}), S_{3} = Eq(ϕ_{3} : Ψ_{3}). From E ⊢ S_{1} ≈ S_{2} and E ⊢ S_{2} ≈ S_{3} it follows that E ⊢ ϕ_{1} ≈ ϕ_{2}, E ⊢ ϕ_{2} ≈ ϕ_{3}, E ⊢ Ψ_{1} ≈ Ψ_{2} and E ⊢ Ψ_{2} ≈ Ψ_{3}. By induction hypothesis we have E ⊢ ϕ_{1} ≈ ϕ_{3} and E ⊢ Ψ_{1} ≈ Ψ_{3} and by (Conv/Eq) E ⊢ Eq(ϕ_{1} : Ψ_{1}) ≈ Eq(ϕ_{3} : Ψ_{3}).

3. Block types. Reflexivity, symmetry and transitivity for block types hold because these properties are true for terms, and term judgments are the only ones appearing in the premises of formation rules for block types. In the proofs of symmetry and transitivity, we need to convert environments (change types to convertible ones) like in the symmetry and transitivity cases for module types.

Lemma 4.1.13. Let E be an environment and ϕ an expression which is neither a module nor a block. Let v_{1} … v_{n} be free variables of ϕ and δ_{1}, δ_{2} substitutions. If E ⊢ v_{i}δ_{1} ≈ v_{i}δ_{2} for every i = 1 … n and E ⊢ ϕδ_{1} : Ψ_{1}, E ⊢ ϕδ_{2} : Ψ_{2} for some Ψ_{1}, Ψ_{2} then E ⊢ ϕδ_{1} ≈ ϕδ_{2}.

Proof. By induction on the structure of ϕ. Let us do the proof for ϕ being a variable, an abstraction, a signature, a functor type and an IndSpec(). For other expressions proofs are similar.

If ϕ is a variable then ϕ = v_{i} and E ⊢ v_{i}δ_{1} ≈ v_{i}δ_{2} holds by assumption about substitutions δ_{1} and δ_{2}.

If ϕ = [v : t]e then E ⊢ ([v : t]e)δ_{1} : Ψ_{1} and E ⊢ ([v : t]e)δ_{2} : Ψ_{2} imply, by Lemma 4.1.3, that E ⊢ tδ_{1} : Ψ′_{1}, E; v : Ty(tδ_{1}) ⊢ eδ_{1} : Ψ′′_{1}, E ⊢ tδ_{2} : Ψ′_{2} and E; v : Ty(tδ_{2}) ⊢ eδ_{2} : Ψ′′_{1} for some Ψ′_{1}, Ψ′′_{1}, Ψ′_{2} and Ψ′′_{2}. By induction hypothesis we know that E ⊢ tδ_{1} ≈ tδ_{2}. Using Lemma 4.1.7 on E; v : Ty(tδ_{1}) ⊢ ok, E; v : Ty(tδ_{2}) ⊢ eδ_{2} : Ψ′′_{1} and E; v : Ty(tδ_{1}) ⊢ v : Ty(tδ_{2}) we get E; v : Ty(tδ_{1}) ⊢ eδ_{2} : Ψ′′_{1}. Note that E; v : Ty(tδ_{1}) ⊢ v : Ty(tδ_{2}) is true by Lemma 4.1.10 and E ⊢ Ty(tδ_{1}) ≈ Ty(tδ_{2}), which follows from E ⊢ tδ_{1} ≈ tδ_{2}. Now, by induction hypothesis E; v : Ty(tδ_{1}) ⊢ eδ_{1} ≈ eδ_{2} and it is sufficient to use (Conv/Abs) to obtain E ⊢ ([v : t]e)δ_{1} ≈ ([v : t]e)δ_{2}.

If ϕ = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} Endthen by Lemma 4.1.8 we have E; \overline{v}_{1} : S_{1}δ_{1} … \overline{v}_{i−1} : S_{i−1}δ_{1} ⊢ S_{i}δ_{1} : spec, E; \overline{v}_{1} : S_{1}δ_{2} … \overline{v}_{i−1} : S_{i−1}δ_{2} ⊢ S_{i}δ_{2} : spec for i = 1 … n . In order to get E; \overline{v}_{1} : S_{1}δ_{1} … \overline{v}_{i−1} : S_{i−1}δ_{1} ⊢ S_{i}δ_{2} : spec for i = 1 … n we will interleave the use of induction hypothesis and Lemma 4.1.11. First, by induction hypothesis E ⊢ S_{1}δ_{1} ≈ S_{1}δ_{2}. Using this equality, by Lemma 4.1.7 and 4.1.11 we get E; \overline{v}_{1} : S_{1}δ_{1} ⊢ S_{2}δ_{2} : spec. By induction hypothesis E; \overline{v}_{1} : S_{1}δ_{1} ⊢ S_{2}δ_{1} ≈ S_{2}δ_{2}. Using this second equality we get E; \overline{v}_{1} : S_{1}δ_{1}, \overline{v}_{2} : S_{2}δ_{1} ⊢ S_{3}δ_{2} : spec. After continuing like this till i = n, we can use (Conv/Sig) to obtain E ⊢ (Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End)δ_{1} ≈ (Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End)δ_{2}.

If ϕ = Funsig(v : M′)M′′ then the proof is similar to the abstraction case, but we need to use Lemma 4.1.10 in order to convert types with (Subsumption) (instead of (Term/Conv) which does not work for modules).

If ϕ = IndSpec(E^{I} := E^{C}) with E^{I} = v_{1}^{I} : Ty(t_{1}) … v_{k}^{I} : Ty(t_{k}), E^{C} = v_{1}^{C} : Ty(t̂_{1}) … v_{k}^{C} : Ty(t̂_{n}) then by Lemma 4.1.3 we have E ⊢ t_{j}δ_{1} : Ψ_{j}^{1}, E ⊢ t_{j}δ_{2} : Ψ_{j}^{2} for j = 1 … k and E; E^{I}δ_{1} ⊢ t̂_{i} : Ψ_{i}^{1}′, E; E^{I}δ_{2} ⊢ t̂_{i} : Ψ_{i}^{2}′ for i = 1 … n for some Ψ_{j}^{1}, Ψ_{j}^{2}, Ψ_{i}^{1}′ and Ψ_{i}^{2}′. By induction hypothesis E ⊢ t_{j}δ_{1} ≈ t_{j}δ_{2} for j = 1 … k. In order to get E; E^{I}δ_{1} ⊢ t̂_{i} : Ψ_{i}^{2}′ from E; E^{I}δ_{2} ⊢ t̂_{i} : Ψ_{i}^{2}′ we have to proceed like in the abstraction case and use conversion for all variables of E^{I}. Afterwards, we can use induction hypothesis, get E; E^{I}δ_{1} ⊢ t̂_{i}δ_{1} ≈ t̂_{i}δ_{2} for i = 1 … n and apply (Conv/IndSpec) to obtain E ⊢ IndSpec(E^{I} := E^{C})δ_{2} ≈ IndSpec(E^{I} := E^{C})δ_{2}.

Apart from subtyping, another operation performed on module types is strengthenning. It is not difficult to understand but quite cumbersome for formal proof of its correctness.

Lemma 4.1.14. For every environment E, module type M, path p and for every specification S and path sequence \overline{p}:

1. if E ⊢ M : modtype and E ⊢ p : M then E ⊢ M/p : modtype and E ⊢ M/p <: M. Moreover if M = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End then for every i = 1 … n

(a) E ⊢ S_{i}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\} : spec

(b) E_{i−1/} ⊢ \overline{v}_{i−1} : S_{i−1}

(c) E_{i−1/} ⊢ S_{i} : spec

(d) E_{i−1/} ⊢ v_{i−1}^{j} ≈ p.v_{i−1}^{j}

(e) E_{i−1/} ⊢ S_{i} ≈ S_{i}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\}

(f) E_{i−1/} ⊢ \overline{p.v_{i}} : S_{i}

(g) E_{i−1/} ⊢ \overline{p.v_{i}} : S_{i}/\overline{p.v_{i}}

where E_{i/} = E, \overline{v}_{1} : S_{1}/\overline{p.v_{1}} … \overline{v}_{i} : S_{i}/\overline{p.v_{i}}.

2. if E ⊢ S : spec and E ⊢ \overline{p} : S then E ⊢ S/\overline{p} : spec, E ⊢ \overline{p} : S/\overline{p}, E; \overline{v} : S/\overline{p} ⊢ \overline{v} : S and E; \overline{v} : S/\overline{p} ⊢ \overline{v} ≈ \overline{p}

Proof. By induction on the structure of M and S.

1. If M = Funsig(v : M′)M′′ then M/p = M and the lemma trivially holds. For signatures (M = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End) let us first do the proofs of the properties (1a) … (1g). These proofs are done by induction on i.

The base step is for i = 1 and it reduces to E ⊢ S_{1} : spec, E ⊢ \overline{p.v_{1}} : S_{1} (true by (Sig/Access) from E ⊢ p : M) and E ⊢ \overline{p.v_{1}} : S_{1}/\overline{p.v_{1}} (true by induction hypothesis (2)). Let us suppose that for i = 1 … k the properties hold and let us do the proof for i = k + 1.

(a) Since E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End : modtype, by (Sig/Access) we obtain E ⊢ \overline{p.v_{j}} : S_{j}\{\overline{v}_{m} ↦ \overline{p.v_{m}}\} for every j = 1 … n and by Lemma 4.1.1 E; \overline{v}_{1} : S_{1} … \overline{v}_{l−1} : S_{l−1} ⊢ S_{l} : spec for every l = 1 … n. Now, it is sufficient to use Lemma 4.1.5 l times and substitute \overline{p.v_{1}} … \overline{p.v_{l}} to \overline{v}_{1} … \overline{v}_{l} respectively (from left to right) in order to get E ⊢ S_{l}\{\overline{v}_{m} ↦ \overline{p.v_{m}}\} : spec for every l = 1 … n.

(b) We have E_{k−1/} ⊢ S_{k} : spec by induction hypothesis (1c) and E_{k−1/} ⊢ \overline{p.v_{k}} : S_{k} by induction hypothesis (1f). Hence by induction hypothesis (2) we get E_{k−1/}; \overline{v}_{k} : S_{k}/\overline{p.v_{k}} ⊢ \overline{v}_{k} : S_{k} which is the desired conclusion.

(c) Since E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End : modtype we know that E; \overline{v}_{1} : S_{1} … \overline{v}_{k} : S_{k} ⊢ S_{k+1} : spec. By induction hypothesis (1b) we know that E_{k/} ⊢ \overline{v}_{j} : S_{j} for j = 1 … k − 1 and we have just proved that E_{k/} ⊢ \overline{v}_{k} : S_{k}. Hence we can use Lemma 4.1.7 and get E_{k/} ⊢ S_{k+1} : spec.

(d) By induction hypothesis (1c) we have E_{k−1}/ ⊢ S_{k} : spec and by induction hypothesis (1f) E_{k−1/} ⊢ \overline{p.v_{k}} : S_{k}. Now, we can use induction hypothesis of (2) and get E_{k−1/}; \overline{v}_{k} : S_{k}/\overline{p.v_{k}} ⊢ \overline{v}_{k} ≈ \overline{p.v_{k}}.

(e) In (1c) we have shown that E_{k/} ⊢ S_{k+1} : spec. By (1a) and Lemma 4.1.4 we have also E_{k/} ⊢ S_{k+1}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\} : spec. Since E_{k/} ⊢ \overline{v}_{j} ≈ \overline{p.v_{j}} for every j = 1 … k (by (1d) and Lemma 4.1.4), we can apply Lemma 4.1.13 and get E_{k/} ⊢ S_{k+1} ≈ S_{k+1}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\}.

(f) By (Sig/Access) applied to E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End : modtype we know that E ⊢ \overline{p.v_{k+1}} : S_{k+1}\{v ↦ \overline{p.v}\}. By Lemma 4.1.4 we have also E_{k/} ⊢ \overline{p.v_{k+1}} : S_{k+1}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\}. In (1e) we have just shown that E_{k/} ⊢ S_{k+1} ≈ S_{k+1}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\} and by symmetry of conversion (Lemma 4.1.12) E_{k/} ⊢ S_{k+1}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\} ≈ S_{k+1}. Consequently, we get E_{k/} ⊢ \overline{p.v_{k+1}} : S_{k+1} by Lemma 4.1.11.

(g) It is an easy corollary from (1f) and (2).

Now, let us show that for M = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End the judgments E ⊢ M/p : modtype and E ⊢ M/p <: M hold. Note that we have M/p = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1}/\overline{p.v_{1}} … \overline{v}_{n}▷\overline{v}_{n} : S_{n}/\overline{p.v_{n}} End.

By (1f) for every i = 1 … n we have E_{i−1/} ⊢ \overline{p.v_{i}} : S_{i}. By induction hypothesis (2) this implies E_{i−1/} ⊢ S_{i}/\overline{p.v_{i}} : spec for every i = 1 … n and E_{n/} ⊢ ok by (Env/Insert). By (Sig/Form) we obtain E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1}/\overline{p.v_{1}} … \overline{v}_{n}▷\overline{v}_{n} : S_{n}/\overline{p.v_{n}} End : modtype.

To show that

E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1}/\overline{p.v_{1}} … \overline{v}_{n}▷\overline{v}_{n} : S_{n}/\overline{p.v_{n}} End <: Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End

it is sufficient that E_{i/} ⊢ \overline{v}_{i} : S_{i} for every i = 1 … n, which is true by (1b).

2. There are two cases for specifications: abstract and manifest.

(a) S = Ty(Ψ). By assumption we know that E ⊢ Ty(Ψ) : spec (which implies E ⊢ Ψ : Ξ) and E ⊢ \overline{p} : Ty(Ψ) (which implies E ⊢ \overline{p} : Ψ).

i. For blocks and terms S/\overline{p} = Eq(\overline{p} : Ψ). Then E ⊢ Eq(\overline{p} : Ψ) : spec holds by (Eq/Form), E ⊢ \overline{p} : Eq(\overline{p} : Ψ) by (Eq/Sat), E; \overline{v} : Eq(\overline{p} : Ψ) ⊢ \overline{v} : Ty(Ψ) by (Env/Lookup) followed by (Eq/Type) and (Ty/Sat) and E; \overline{v} : Eq(\overline{p} : Ψ) ⊢ \overline{v} ≈ \overline{p} by (Env/Lookup) and (Eq/Comp).

ii. For modules Ψ = M and S/p = Eq(p : M/p). Then E ⊢ Eq(p : M/p) : spec holds because by induction hypothesis (1) we have E ⊢ M/p : modtype and E ⊢ p : Eq(p : M/p) holds by strengthening of E ⊢ p : M. The judgment E; v : Eq(p : M/p) ⊢ v : Ty(M) is true by (Env/Lookup) (for v) followed by (Eq/Type), (Subsumption) (as E ⊢ M/p <: M by (1)) and (Ty/Sat). As above, E; v : Eq(p : Ψ) ⊢ v ≈ p follows from (Env/Lookup) and (Eq/Comp).

(b) S = Eq(ϕ : Ψ). By assumption we know that E ⊢ Eq(ϕ : Ψ) : spec (which implies E ⊢ Ψ : Ξ and E ⊢ ϕ : Ψ) and E ⊢ \overline{p} : Eq(ϕ : Ψ) (which implies E ⊢ \overline{p} : Ψ and E ⊢ \overline{p} ≈ ϕ). i. For blocks and terms S/\overline{p} = Eq(ϕ : Ψ). The only nontrivial property to

prove is E; \overline{v} : Eq(ϕ : Ψ) ⊢ \overline{v} ≈ \overline{p}. But it follows from symmetry and transitivity of ≈ for paths applied to the assumption E ⊢ \overline{p} ≈ ϕ and to E; \overline{v} : Eq(ϕ : Ψ) ⊢ \overline{v} ≈ ϕ (which follows from E; \overline{v} : Eq(ϕ : Ψ) ⊢ \overline{v} : Eq(ϕ : Ψ) by (Eq/Comp)).

ii. For modules ϕ = p′, Ψ = M and S/p = Eq(p′ : M/p). To show that E ⊢ Eq(p′ : M/p) : spec we need to know that E ⊢ M/p : modtype and E ⊢ p′ : M/p. The former judgment follows from (1), since E ⊢ p : M. The latter one follows by (Subsumption) from E ⊢ p′ : M/p′ (which is true by (1)) and E ⊢ M/p′ <: M/p (which is true by Lemma 4.1.13 applied to E ⊢ M/p′ : spec, E ⊢ M/p : spec and E ⊢ p′ ≈ p). The judgment E ⊢ p : Eq(p′ : M/p) holds by (Eq/Sat), because E ⊢ p : M/p from (1) and E ⊢ p ≈ p′. The judgment E; v : Eq(p′ : M/p) ⊢ v : Eq(p′ : M) holds by (Eq/Sat), because E; v : Eq(p′ : M/p) ⊢ v : M/p by (Env/Lookup) and (Eq/Type) and E ⊢ M/p <: M by induction hypothesis (1). To prove that E; v : Eq(p′ : M/p) ⊢ v ≈ p it is sufficient to use symmetry and transitivity of ≈ on E ⊢ p ≈ p′ (which is an assumption) and E; v : Eq(p′ : M) ⊢ v ≈ p′ (which follows from E; v : Eq(p′ : M/p) ⊢ v : Eq(p′ : M/p) by (Eq/Comp).

The following lemma shows that our typing rules assign correct module types, block types and specifications to expressions. Note that we cannot prove the analogue fact for terms at this point. A simple inductive proof of this fact requires that all judgments are closed under arbitrary term substitutions, which is not true in our system. However, when we limit ourselves to principal derivations, the remaining correctness property of term typing can also be established (see Lemma 5.10.1).

Lemma 4.1.15. For every environment E the following implications hold:

• if E ⊢ ϕ : S then E ⊢ S : spec

• if E ⊢ P : BT then E ⊢ BT : blocktype

• if E ⊢ m : M then E ⊢ M : modtype

• if E ⊢ S_{1} ≈ S_{2} then E ⊢ S_{1} : spec and E ⊢ S_{2} : spec

• if E ⊢ BT_{1} ≈ BT_{2} then E ⊢ BT_{1} : blocktype and E ⊢ BT_{2} : blocktype

• if E ⊢ M_{1} ≈ M_{2} then E ⊢ M_{1} : modtype and E ⊢ M_{2} : modtype

• if E ⊢ M_{1} <: M_{2} then E ⊢ M_{2} : modtype Proof. By induction on the derivation of the judgment considered. In most cases this follows directly from the premises of the typing rules and sometimes from the induction hypothesis. Let us see the rules where more work is needed:

For (Env/Lookup)

\frac{E_{1}; \overline{v} : S; E_{2} ⊢ ok}{E_{1}; \overline{v} : S; E_{2} ⊢ \overline{v} : S}

by Lemma 4.1.1 applied to E_{1}; \overline{v} : S; E_{2} ⊢ ok we get E_{1} ⊢ S : spec and by Lemma 4.1.4 E_{1}; \overline{v} : S; E_{2} ⊢ S : spec.

For (Sig/Access)

E ⊢ p : Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End
E ⊢ \overline{p.v_{k}} : S_{k}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\}

we have E ⊢ S_{k}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\} : spec by Lemma 4.1.14 (1a).

For (Funsig/Sub)

\frac{E ⊢ M′_{2} <: M′_{1} E; v : Ty(M′_{2}) ⊢ M′′_{1} <: M′′_{2}}{E ⊢ Funsig(v : M′_{1})M′′_{1} <: Funsig(v : M′_{2})M′′_{2}}

by induction hypothesis we have E; v : Ty(M′_{2}) ⊢ M′′_{2} : modtype. By Lemma 4.1.2 in the derivation of E; v : Ty(M′_{2}) ⊢ M′′_{2} : modtype there is a subderivation of E ⊢ Ty(M′_{2}) : spec. Since this judgment can be obtained only by (Ty/Form) rule, we have also E ⊢ M′_{2} : modtype. From E ⊢ M′_{2} : modtype and E; v : Ty(M′_{2}) ⊢ M′′_{2} : modtype by (Funsig/Form) we get E ⊢ Funsig(v : M′_{2})M′′_{2} : modtype.

For (Mod/App)

\frac{E ⊢ p : Funsig(v : M_{1})M_{2} E ⊢ p′ : M_{1}}{E ⊢ p p′ : M_{2}\{v ↦ p′\}}

by induction hypothesis E ⊢ Funsig(v : M_{1})M_{2} : modtype. Since the judgment of this form can be obtained only by (Funsig/Form) we have also E; v : Ty(M_{1}) ⊢ M_{2} : modtype. By (Ty/Sat) E ⊢ p′ : M_{1} implies E ⊢ p′ : Ty(M_{1}). Now, we can use Lemma 4.1.5 and get E ⊢ M_{2}{v ↦ p′} : modtype.

For (Strengthening)

\frac{E ⊢ p : M}{E ⊢ p : M/p}

we have E ⊢ M/p : modtype by Lemma 4.1.14 (1).

For (Ty/Type)

\frac{E ⊢ P : Ty(Ψ)}{E ⊢ P : Ψ}

by induction hypothesis we have E ⊢ Ty(Ψ) : spec. Since this judgment can be obtained only by (Ty/Form), we have also E ⊢ Ψ : Ξ, which is E ⊢ Ψ : modtype for modules and E ⊢ Ψ : blocktype for blocks.

4.2 Inversion

The following lemma shows that typing of module expressions can be inverted. This means in particular that structures, functors and module applications have principal types. Typing of the last kind of module expressions, access paths, is studied in detail in the next chapter.

Lemma 4.2.1. Let E ⊢ m : M.

• if m = Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End then

E ⊢ m : SIG(m)

and

E ⊢ SIG(m) <: M

where SIG(m) = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End is the primary signature of m. Moreover, E; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ P_{i} : S_{i} holds for all i.

• if m = Functor[v : M_{1}]m′ then

E ⊢ m : Funsig(v : M_{1})M_{2}

for some M_{2} and

E ⊢ Funsig(v : M_{1})M_{2} <: M

Moreover, E; v : M_{1} ⊢ m′ : M_{2} holds.

• if m = p_{1} p_{2} then

E ⊢ p_{1} : Funsig(v : M_{1})M_{2}

for some M_{1} and M_{2} such that E ⊢ p_{2} : M_{1} and E ⊢ M_{2}{v ↦ p_{2}} <: M.

Proof. It we examine the application of the rule which introduced the module expression m in the derivation of E ⊢ m : M, it must have been (Sig/Struct), (Funsig/Functor) or (Mod/App) depending on the syntax of m. After that rule only the (Subsuption) rule could have been applied. Using the premises of the syntax-oriented rule, the sequence of subtyping judgments used by subsumption rules and the fact that subtyping is reflexive and transitive (Lemmas 4.1.9 and 4.1.10), we get all the expected conclusions.

Principal typing

The typing system studied in this chapter is a sub-system of the one presented in Chapter 2. The derivations in this system are called principal, because they derive principal module types for module paths, as shown in Lemma 5.6.5.

Apart from typing rules for module paths, the system contains all typing rules for terms and elimination and computation rules for blocks. Lemma 5.8.7 shows that all valid term judgments in ⊢ are also derivable in ⊢_{P}, which means in particular that module subtyping is not essential for deriving term judgments.

The most important consequence of the latter property is the possibility to eliminate module entries from environments if the judgment considered does not concern modules (Lemmas 5.9.1 and 5.9.2). This operation is essential in the flattening phase of the module elimination process described in Chapter 3.

5.1 Paths

From all rules for environment and modules, we keep only those which serve to extract information from the environment or a signature. Note that the correctness of the environment in the first rule is established using the whole ⊢ system, and that there is no corresponding E ⊢_{P} ok judgment.

(P/Env/Lookup)  \frac{E_{1}; \overline{v} : S; E_{2} ⊢ ok}{E_{1}; \overline{v} : S; E_{2} ⊢_{P} \overline{v} : S}

(P/Sig/Access) (E ⊢_{P} p : Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End) / (E ⊢_{P} \overline{p.v_{i}} : S_{i}\{\overline{v}_{i} ↦ \overline{p.v_{i}}\})

5.2 Specifications

The principal rules for specifications are also only those which say how to use specifications, i.e. elimination and computation rules.

Abstract specifications

(P/Ty/Type)  \frac{E ⊢_{P} \overline{p} : Ty(Ψ)}{E ⊢_{P} \overline{p} : Ψ}

Manifest specifications

(P/Eq/Type)  \frac{E ⊢_{P} \overline{p} : Eq(ϕ : Ψ)}{E ⊢_{P} \overline{p} : Ψ}
(P/Eq/Comp)  \frac{E ⊢_{P} \overline{p} : Eq(ϕ : Ψ)}{E ⊢_{P} \overline{p} ≈ ϕ}

where ϕ and Ψ are respectively: e and t, \overline{p} and BT or p and M.

5.3 Terms

The rules in this section correspond to the rules for pure type systems. They are isomorphic to those given in Section 2.4.

(P/Term/Prod) \frac{E ⊢_{P} t_{1} : s_{1} E; v : Ty(t_{1}) ⊢_{P} t_{2} : s_{2}}{E ⊢_{P} (v:t_{1}) t_{2} : s_{3}} if (s_{1}, s_{2}, s_{3}) ∈ R

(P/Term/Abs)  \frac{E; v : Ty(t_{1}) ⊢_{P} e : t_{2} E ⊢_{P} (v:t_{1}) t_{2} : s}{E ⊢_{P} [v:t_{1}]e : (v:t_{1}) t_{2}}
(P/Term/Axiom) \frac{E ⊢ ok}{E ⊢_{P} s_{1} : s_{2}} if (s_{1}, s_{2}) ∈ A
(P/Term/App)  \frac{E ⊢_{P} e : (v:t_{1}) t_{2} E ⊢_{P} e′ : t_{1}}{E ⊢_{P} e e′ : t_{2}\{v ↦ e′\}}
(P/Term/Beta) [v:t]e e′ ⟶_{β} e{v ↦ e′}
(P/Term/Conv)  \frac{E ⊢_{P} e : t E ⊢_{P} t′ : s E ⊢_{P} t ≈ t′}{E ⊢_{P} e : t′}

5.4 Blocks

The principal typing rules for blocks contain again only elimination and computation rules.

Rewrite blocks In the forthcoming rules E^{Γ} = v_{1} : Ty(t_{1}) … v_{n} : Ty(t_{n}) and R = {E_{i} | e_{i}^{L} ⟶ e_{i}^{R} : t̂_{i}}_{i=1...m}, where E_{i} = v_{1}^{i} : Ty(t_{1}^{i}); …; v_{n_{i}}^{i} : Ty(t_{n_{i}}^{i})

(P/RewSpec/Fun)  \frac{E ⊢_{P} \overline{p} : RewSpec(E^{Γ}, R)}{E ⊢_{P} p^{j} : t_{j}}

(P/RewSpec/Comp)

\frac{E ⊢_{P} \overline{p} : RewSpec(E^{Γ}, R) δ : E_{i} → E}{E ⊢_{P} e_{i}^{L}θδ ≈ e_{i}^{R}θδ} where θ = \{v_{j} ↦ p^{j}\}_{j=1...n}

The notation δ : E_{i} → E means that for every v : Ty(t) ∈ E_{i} we have E ⊢_{P} vδ : tδ.

Inductive blocks Let E^{I} = v_{1}^{I} : Ty(t_{1}) … v_{k}^{I} : Ty(t_{k}), E^{C} = v_{1}^{C} : Ty(t̂_{1}) … v_{n}^{C} : Ty(t̂_{n}) (P/IndSpec/IndType) \frac{E ⊢_{P} \overline{p} : IndSpec(E^{I} := E^{C})}{E ⊢_{P} p^{j} : t_{j}} for j = 1 … k

(P/IndSpec/Constr) \frac{E ⊢_{P} \overline{p} : IndSpec(E^{I} := E^{C})}{E ⊢_{P} p^{k+i} : t̂_{i}\{v_{j}^{I} ↦ p^{j}\}_{j=1...k}} for i = 1 … n

5.5 Convertibility

Convertibility rules in the principal system concern only terms and paths. General

(P/Conv/Trans) \frac{E ⊢_{P} ϕ_{1} ≈ ϕ_{2} E ⊢_{P} ϕ_{2} ≈ ϕ_{3}}{E ⊢_{P} ϕ_{1} ≈ ϕ_{3}} (P/Conv/Sym) \frac{E ⊢_{P} ϕ_{1} ≈ ϕ_{2}}{E ⊢_{P} ϕ_{2} ≈ ϕ_{1}}

where ϕ_{1}, ϕ_{2}, ϕ_{3} are terms or paths. Paths

(P/Conv/Refl)  \frac{E ⊢_{P} v : M}{E ⊢_{P} v ≈ v}
(P/Conv/Path)  \frac{E ⊢_{P} p.v : Ψ E ⊢_{P} p ≈ p′ E ⊢_{P} p′.v : Ψ′}{E ⊢_{P} p.v ≈ p′.v}

where Ψ and Ψ′ are module types or terms.

Terms

(P/Conv/Beta)  \frac{E ⊢_{P} e_{1} : t e_{1} =_{β} e_{2} E ⊢_{P} e_{2} : t′}{E ⊢_{P} e_{1} ≈ e_{2}}

(P/Conv/App)  \frac{E ⊢_{P} e_{1} e_{2} : t E ⊢_{P} e_{1} ≈ e′_{1} E ⊢_{P} e_{2} ≈ e′_{2} E ⊢_{P} e′_{1} e′_{2} : t′}{E ⊢_{P} e_{1} e_{2} ≈ e′_{1} e′_{2}}

(P/Conv/Prod)

\frac{E ⊢_{P} (v:t_{1}) t_{2} : t E ⊢_{P} t_{1} ≈ t′_{1} E; v : Ty(t_{1}) ⊢_{P} t_{2} ≈ t′_{2} E ⊢_{P} (v:t′_{1}) t′_{2} : t′}{E ⊢_{P} (v:t_{1}) t_{2} ≈ (v:t′_{1}) t′_{2}}

(P/Conv/Abs)

\frac{E ⊢_{P} [v:t]e : t̂ E ⊢_{P} t ≈ t′ E; v : Ty(t) ⊢_{P} e ≈ e′ E ⊢_{P} [v:t′]e′ : t̂′}{E ⊢_{P} [v:t]e ≈ [v:t′]e′}

5.6 Principal module types

The following facts help establish the correctness of principal typing. We show that a principal type of a module path is indeed principal: after strengthening it is a subtype of all other types the module path can have.

Lemma 5.6.1. For every module type M, specification S and paths p_{1} and p_{2} such that E ⊢ p_{1} ≈ p_{2}, the following two implications hold:

• if E ⊢ p_{1} : M and E ⊢ p_{2} : M then E ⊢ M/p_{1} <: M/p_{2},

• if E ⊢ p_{1} : S and E ⊢ p_{2} : S then E; v : S/p_{1} ⊢ v : S/p_{2}.

Proof. By induction on the structure of M and S respectively. If M is a functor type the result is trivial. Otherwise, M is a signature Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End. We have to show that E ⊢ M/p_{1} : modtype, E ⊢ M/p_{2} : modtype which follow from Lemma 4.1.14 and

E; \overline{v}_{1} : S_{1}/\overline{p_{1}.v_{1}} … \overline{v}_{n} : S_{n}/\overline{p_{1}.v_{n}} ⊢ \overline{v}_{i} : S_{i}/\overline{p_{2}.v_{i}} for i = 1 … n

The latter can be easily obtained using Lemma 4.1.14, the rule (Conv/Path), induction hypothesis and Lemma 4.1.4.

For specifications, the desired conclusion is immediate for term and block specifications and follows from induction hypothesis for module specifications.

Lemma 5.6.2. If E ⊢ p : M_{1} and E ⊢ M_{1} <: M_{2} then E ⊢ M_{1}/p <: M_{2}/p Proof. If M_{1} is a functor type then so is M_{2}. In this case strengthening does nothing and the lemma is trivial. Otherwise, if M_{1} is a signature Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End, then M_{2} is also a signature. Let M_{2} = Sig \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} … \overline{v′}_{m}▷\overline{v′}_{m} : S′_{m} End. By inversion of the rule (Sig/Sub) necessarily used to derive E ⊢ M_{1} <: M_{2}, we get E ⊢ M_{1} : modtype, E ⊢ M_{2} : modtype and

E; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ \overline{v}′_{j} : S′_{j} for j = 1 … m

What we have to show is E ⊢ M_{1}/p : modtype, E ⊢ M_{2}/p : modtype and

E; \overline{v}_{1} : S_{1}/\overline{p.v_{1}} … \overline{v}_{n} : S_{n}/\overline{p.v_{n}} ⊢ \overline{v}′_{j} : S′_{j}/\overline{p.v′_{j}} for j = 1 … m

The correctness of module types easily follows from Lemma 4.1.14 and (Subsumption). To prove the correctness of the remaining judgments let us fix an arbitrary j. We present the proof for the case S′_{j} is a module specification.

Let us abbreviate E; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} by E_{1} and E; \overline{v}_{1} : S_{1}/\overline{p.v_{1}} … \overline{v}_{n} : S_{n}/\overline{p.v_{n}} by E_{1/}. Similarly, let E_{2} denote E; \overline{v}′_{1} : S′_{1} … \overline{v}′_{m} : S′_{m} and E_{2/} the environment E; \overline{v}′_{1} : S′_{1}/\overline{p.v′_{1}} … \overline{v}′_{m} : S′_{m}/\overline{p.v′_{m}}. From correctness of corresponding signatures it follows that all four environments are correct.

If S′_{j} is an abstract specification Ty(M), then S′_{j}/p.v′_{j} = Eq(p.v′_{j} : M/p.v′_{j}). Since E_{1/} subsumes E_{1}, we know that E_{1/} ⊢ v′_{j} : Ty(M). Using Lemma 4.1.14 for M_{1} and M_{2} followed by Lemma 4.1.4 and Lemma 4.1.7, we obtain E_{1/} ⊢ p.v′_{j} : Ty(M) and E_{1/} ⊢ v′_{j} ≈ p.v′_{j}.

Now using E_{1/} ⊢ M/v′_{j} <: M/p.v′_{j} by Lemma 5.6.1, we can show that E_{1/} ⊢ v′_{j} : M/p.v′_{j}. Using (Eq/Sat) we get E_{1/} ⊢ v′_{j} : Eq(p.v′_{j} : M/p.v′_{j}).

If S′_{j} is a manifest specification Eq(p′ : M) then S′_{j}/p.v′_{j} = Eq(p′ : M/p.v′_{j}). We can show that E_{1/} ⊢ v′_{j} : M/p.v′_{j} in the same way as before and the fact that E_{1/} ⊢ v′_{j} ≈ p′ follows from the assumption and Lemma 4.1.7. It is again enough to apply (Eq/Sat) to get the desired conclusion.

The lemma below shows that the principal type is correct for variables.

Lemma 5.6.3. Let S = Ty(M) or Eq(p : M). If E; v : S; E′ ⊢ v : M′ then E; v : S; E′ ⊢ M/v <: M′.

Proof. We prove by induction a slightly stronger fact: if E; v : S; E′ ⊢ J where J = v : M′, v : Ty(M′), v : Eq(p′ : M′) or Eq(v : M′) : spec, then E; v : S; E′ ⊢ M/v <: M′.

We proceed by induction on the derivation of E; v : S; E′ ⊢ J. The rule (Env/Lookup) is covered by Lemma 4.1.14. For specification formation, introduction and elimination rules, we directly use induction hypothesis. The other rules which can assign a module type to a variable, (Subsumption) and (Strengthening), are treated by Lemma 4.1.9 and Lemma 5.6.2 respectively.

Lemma 5.6.4. Let E ⊢ p : M where M = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End and S_{i} = Ty(M_{i}) or Eq(p̂: M_{i}). If E ⊢ M <: Sig \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} … \overline{v′}_{m}▷\overline{v′}_{m} : S′_{m} End and there exists j such that v′_{j} = v_{i} then S′_{j} = Ty(M′_{j}) or S′_{j} = Eq(p̂′ : M′_{j}) where E ⊢ M_{i}\{\overline{v}_{k} ↦ \overline{p.v_{k}}\}/p.v_{i} <: M′_{j}\{\overline{v}′_{k} ↦ \overline{p.v′_{k}}\}.

Proof. By inverting the subtyping judgment, we know that

E; \overline{v}_{1} : S_{1} … v_{i} : S_{i} … \overline{v}_{n} : S_{n} ⊢ v_{i} : S′_{j}

By Lemma 5.6.3, we have

E; \overline{v}_{1} : S_{1} … v_{i} : S_{i} … \overline{v}_{n} : S_{n} ⊢ M_{i}/v_{i} <: M′_{j}

and we conclude by applying n times Lemma 4.1.5, substituting p.v_{k} on v_{k} for all k = 1 … n. Of course M_{i}\{\overline{v}_{k} ↦ \overline{p.v_{k}}\}/p.v_{i} = M_{i}/v_{i}\{\overline{v}_{k} ↦ \overline{p.v_{k}}\}.

Lemma 5.6.5. For every environment E, path p and module type M, if E ⊢ p : M then there exists exactly one module type M_{P} such that E ⊢_{P} p : M_{P} and E ⊢ M_{P} /p <: M.

Proof. We use the same trick as in Lemma 5.6.3. We prove by induction on the derivation that if E ⊢ J where J = p : M′, p : Ty(M′), p : Eq(p′ : M′) or Eq(p : M′) : spec, then E ⊢ M_{P} /p <: M′.

If the last rule was (Env/Lookup), then we get the following E; v : S; E′ ⊢ v : S where S = Ty(M) or Eq(p′ : M). By (P/Env/Lookup) followed by (P/Ty/Type) or (P/Eq/Type), we get E; v : S; E′ ⊢_{P} v : M, and by Lemma 4.1.14 we know that E; v : S; E′ ⊢ M/v <: M.

Specification formation, satisfaction or elimination rules are immediately treated by induction hypothesis.

If the last rule was (Sig/Access), we have:

E ⊢ p : Sig \overline{v}′_{1}▷\overline{v}′_{1} : S′_{1} … \overline{v}′_{m}▷\overline{v}′_{m} : S′_{m} End
E ⊢ p.v′_{j} : S_{j}\{\overline{v}′_{k} ↦ \overline{p.v′_{k}}\}

where S′_{j} = Ty(M′_{j}) or Eq(p′ : M′_{j}). By induction hypothesis, we have E ⊢_{P} p : M_{P} and E ⊢ M_{P} /p <: Sig \overline{v}′_{1}▷\overline{v}′_{1} : S′_{1} … \overline{v}′_{m}▷\overline{v}′_{m} : S′_{m} End. Consequently, M_{P} must be a signature Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End, having a module component v_{i}▷v_{i} : S_{i} where S_{i} = Ty(M_{i}) or Eq(p′′ : M_{i}) and v_{i} = v′_{j}. We can use the rule (P/Sig/Access) followed by (P/Ty/Type) or (P/Eq/Type) to get E ⊢_{P} p.v_{i} : M_{i}\{\overline{v}_{k} ↦ \overline{p.v_{k}}\} and Lemma 5.6.4 to get E ⊢_{P} M_{i}\{\overline{v}_{k} ↦ \overline{p.v_{k}}\}/p.v_{i} <: M′_{j}\{\overline{v}_{k} ↦ \overline{p.v_{k}}\}.

The rules (Strengthenning) and (Subsumption) are treated by Lemma 4.1.9 and Lemma 5.6.2 respectively.

Lemma 5.6.6. For every environment E, a path p and Ψ, which is a module type or term, if E ⊢ p : Ψ then E ⊢_{P} p : Ψ′ for some Ψ′.

Proof. If p = v then E ⊢ v : Ψ implies that there is a sequence of variables \overline{v}′ and a specification S such that \overline{v}′ : S ∈ E and v ∈ \overline{v}′. By (P/Env/Lookup) E ⊢_{P} \overline{v}′ : S. Using (P/Ty/Type) or (P/Eq/Type) we get E ⊢_{P} \overline{v} : Ψ̂. If Ψ̂ is a block type, we apply (P/RewSpec/Fun), (P/IndSpec/IndType) or (P/IndSpec/Constr) to get E ⊢_{P} v : t for some term t. If Ψ̂ is a module type or a term, we are done.

Otherwise p = p′.v_{i}. In the derivation of E ⊢ p′.v_{i} : Ψ there must be an instance of (Sig/Access) of the following form

E ⊢ p′ : Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End
E ⊢ \overline{p′.v_{i}} : S′_{i}\{\overline{v}_{j} ↦ \overline{p′.v_{j}}\}

By Lemma 5.6.5 we have E ⊢_{P} p′ : M_{P} and E ⊢ M_{P} /p′ <: Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End. From the subtyping judgment it follows that M_{P} is a signature and that it has a label v_{i}. Consequently, we can use (P/Sig/Access) and then (P/Ty/Type) or (P/Eq/Type) and optionally (P/RewSpec/Fun), (P/IndSpec/IndType) or (P/IndSpec/Constr) to get E ⊢_{P} p′.v_{i} : Ψ′ for some Ψ′.

5.7 Properties of principal derivations

In this section we establish some basic metatheoretical properties of principal derivations, the most important of which is closure by term substitutions (Lemma 5.7.5). This lemma follows the pattern that is also followed by many other lemmas about operations transforming judgments E ⊢_{P} J into E′ ⊢_{P} J′: if E ⊢ J and E′ ⊢ ok then E′ ⊢ J′.

The proof is done by induction on the derivation of E ⊢_{P} J by inspecting all possible typing rules. In order to use the induction hypothesis for premises of a given typing rule, one must assure that the corresponding environment is correct. Fortunately, in most cases this environment is simply equal E′, and the other cases concern only rules for term abstraction and product, which are handled with the help of Lemmas 5.7.3 and 5.7.4.

Lemma 5.7.1. For every environment E and judgment J, if E ⊢_{P} J then E ⊢ J.

Proof. This is obvious, since the set of typing rules for ⊢_{P} is a subset of the set of rules for ⊢.

Lemma 5.7.2. If E_{1}; E_{2} ⊢ ok and E_{1}; E_{3} ⊢_{P} J then E_{1}; E_{2}; E_{3} ⊢_{P} J.

Proof. By lemma 4.1.4, we know that E_{1}; E_{2}; E_{3} ⊢ ok. By easy induction on the derivation of E_{1}; E_{3} ⊢_{P} J, we get the desired conclusion.

Lemma 5.7.3. In every derivation of E ⊢_{P} (v:t_{1}) t_{2} : t there are subderivations of E ⊢_{P} t_{1} : s_{1} and E; v : Ty(t_{1}) ⊢_{P} t_{2} : s_{2} for some sorts s_{1} and s_{2}. Moreover, either E ⊢ t ≈ s_{3} or t = s_{3} for some s_{3} such that (s_{1}, s_{2}, s_{3}) ∈ R.

Proof. By induction on the derivation. The only typing rules that could result in the judgment E ⊢_{P} (v:t_{1})t_{2} : t are product formation and conversion. In the first case, E ⊢_{P} t_{1} : s_{1} and E; v : Ty(t_{1}) ⊢_{P} t_{2} : s_{2} are the premises and t = s_{3} for some sort s_{3} such that (s_{1}, s_{2}, s_{3}) ∈ R. In the second case, we use the induction hypothesis.

Lemma 5.7.4. If E ⊢_{P} [v:t]e : t′ then E ⊢_{P} t : s for some sort s.

Proof. Straightforward induction, just like in the previous lemma.

Lemma 5.7.5. Let E_{1}, E_{2} and E_{3} be environments such that E_{2} = v_{1} : Ty(t_{1}) … v_{n} : Ty(t_{n}). Let δ be a substitution of the domain {v_{1} … v_{n}} such that E_{1} ⊢_{P} v_{i}δ : t_{i}δ. If E_{1}; E_{3}δ ⊢ ok and E_{1}; E_{2}; E_{3} ⊢_{P} J then E_{1}; E_{3}δ ⊢_{P} Jδ, unless J is of the form v_{i} : S for some specification S and i ∈ {1 … n}.

Proof. The proof is done by induction on the derivation of E_{1}; E_{2}; E_{3} ⊢_{P} J.

If the last rule in the derivation is (Env/Lookup)

\frac{Ê_{1}; \overline{v} : S; Ê_{2} ⊢ ok}{Ê_{1}; \overline{v} : S; Ê_{2} ⊢_{P} \overline{v} : S}

where Ê_{1}; \overline{v} : S; Ê_{2} = E_{1}; E_{2}; E_{3}, then there are two possibilities: either \overline{v} : S ∈ E_{2} or \overline{v} : S ∉ E_{2}. In the former case we have nothing to show since the conclusion has the form v_{i} : S for some i. If \overline{v} : S ∉ E_{2} then we can use (P/Env/Lookup) on E_{1}; E_{3}δ ⊢ ok in order to get E_{1}; E_{3}δ ⊢_{P} \overline{v}δ : Sδ.

If the last rule in the derivation is (P/Sig/Access) then by induction hypothesis we know that E_{1}; E_{3}δ ⊢_{P} pδ : (Sig \overline{v}′_{1}▷\overline{v}′_{1} : S_{1} … \overline{v}′_{n′}▷\overline{v}′_{n′} : S_{n′} End)δ. Since δ is a term substitution and p is a module path (starting with a module variable), pδ = p. Consequently, we can use (P/Sig/Access) to obtain E_{1}; E_{3}δ ⊢ \overline{p.v′_{k}} : S_{k}δ\{\overline{v}′_{j} ↦ \overline{p.v′_{j}}\} which is the desired conclusion as S_{k}δ\{\overline{v}′_{j} ↦ \overline{p.v′_{j}}\} = S_{k}\{\overline{v}′_{j} ↦ \overline{p.v′_{j}}\}δ.

For (P/Ty/Type), by induction hypothesis we know that E_{1}; E_{3}δ ⊢_{P} \overline{p}δ : Ty(Ψ)δ. If \overline{p}δ = \overline{p} then we can use (P/Ty/Type) to get E_{1}; E_{3}δ ⊢_{P} \overline{p}δ : Ψδ. Otherwise \overline{p} = v_{i} and Ψ = t_{i}. In that case E_{1}; E_{3}δ ⊢_{P} v_{i}δ : tδ by assuption and Lemma 5.7.2.

For (P/Eq/Type) and (P/Eq/Comp), by induction hypothesis E_{1}; E_{3}δ ⊢_{P} \overline{p}δ : Eq(ϕ : Ψ)δ holds. Since variables that have abstract specification in the environment (like v_{i} : Ty(t_{i}) ∈ E_{2}) cannot get manifest specifications in the principal system, we conclude that \overline{p}δ = \overline{p}. Then by (P/Eq/Type) or (P/Eq/Comp) we get the desired conclusions.

For (P/Term/Prod)

\frac{E_{1}; E_{2}; E_{3} ⊢_{P} t_{1} : s_{1} E_{1}; E_{2}; E_{3}; v : Ty(t_{1}) ⊢_{P} t_{2} : s_{2}}{E_{1}; E_{2}; E_{3} ⊢_{P} (v : t_{1})t_{2} : s_{3}}

by induction hypothesis we have E_{1}; E_{3}δ ⊢_{P} t_{1}δ : s_{1}. To use induction hypothesis on E_{1}; E_{2}; E_{3}; v : Ty(t_{1}) ⊢_{P} t_{2} : s_{2} we need to know that E_{1}; E_{3}δ; v : Ty(t_{1}δ) ⊢ ok. But E_{1}; E_{3}δ ⊢_{P} t_{1}δ : s_{1} implies E_{1}; E_{3}δ ⊢ t_{1}δ : s_{1}, and by (Ty/Form), E_{1}; E_{3}δ ⊢ Ty(t_{1}δ) : spec. Hence, by (Env/Insert) E_{1}; E_{3}δ; v : Ty(t_{1}δ) ⊢ ok. Once we have E_{1}; E_{3}δ ⊢_{P} t_{1}δ : s_{1} and E_{1}; E_{3}δ; v : Ty(t_{1}δ) ⊢_{P} t_{2}δ : s_{2} we can use (P/Term/Prod) and get E_{1}; E_{3}δ ⊢_{P} (v : t_{1}δ)t_{2}δ : s_{3}.

For (P/Term/Abs) the situation is similar. By induction hypothesis we have E_{1}; E_{3}δ ⊢_{P} (v : t_{1}δ)t_{2}δ : s_{2}. By Lemma 5.7.3 E_{1}; E_{3}δ ⊢_{P} t_{1}δ : s for some sort s. Using this judgment, we prove E_{1}; E_{3}δ; v : Ty(t_{1}δ) ⊢ ok like above and consequently, by induction hypothesis, we get E_{1}; E_{3}δ; v : Ty(t_{1}δ) ⊢_{P} eδ : t_{2}δ. Finally by (P/Term/Abs) we obtain E_{1}; E_{3}δ ⊢_{P} [v : t_{1}δ]eδ : (v : t_{1}δ)t_{2}δ.

The proofs for other term rules are similar but simpler, since environments in the premises and in the conclusion are the same.

Let us now consider (P/RewSpec/Fun)

\frac{E_{1}; E_{2}; E_{3} ⊢_{P} \overline{p} : RewSpec(E^{Γ}, R)}{E_{1}; E_{2}; E_{3} ⊢_{P} p^{j} : t_{j}}

We claim that \overline{p}δ = \overline{p}. Indeed, let us suppose that p^{i}δ ≠ p^{i} for some i. It means that p^{i} = v.v_{1} … v_{n} and v ∈ E_{2}. Since E_{2} is a term environment p^{i} = v. But in the principal system it is not possible that a variable satisfies a specification that is different from the one assigned to it in the environment (in particular, the only way to satisfy a RewSpec() is to deduce it from the environment). Hence, the assumption that p^{i}δ ≠ p^{i} for some i is false and \overline{p}δ = \overline{p}. Consequently, by (P/RewSpec/Fun) E_{1}; E_{3}δ ⊢_{P} p^{j} : t_{j}δ which is the desired conclusion.

For (P/RewSpec/Comp)

\frac{E_{1}; E_{2}; E_{3} ⊢_{P} \overline{p} : RewSpec(E^{Γ}, R) ξ : Ê_{i} → E_{1}; E_{2}; E_{3}}{E_{1}; E_{2}; E_{3} ⊢_{P} e_{i}^{L}θξ ≈ e_{i}^{R}θξ} where θ = \{v′_{j} ↦ p^{j}\}_{j=1...n}

where E^{Γ} = v′_{1} : Ty(t_{1}) … v′_{n} : Ty(t_{n}) and R = {Ê_{i} | e_{i}^{L} ⟶ e_{i}^{R} : t̂_{i}}_{i=1...m}, by induction hypothesis we know that E_{1}; E_{3}δ ⊢_{P} \overline{p}δ : RewSpec(E^{Γ}δ, Rδ) and ξδ : Ê_{i} → E_{1}; E_{3}δ. First, we deduce that \overline{p}δ = \overline{p}, like above. Then, we can use (P/RewSpec/Comp) with ξδ and θδ to get E_{1}; E_{3}δ ⊢_{P} (e_{i}^{L}δ)(θδ)(ξδ) ≈ (e_{i}^{R}δ)(θδ)(ξδ) which is equivalent to E_{1}; E_{3}δ ⊢_{P} (e_{i}^{L}θξ)δ ≈ (e_{i}^{R}θξ)δ.

The proofs for (P/IndSpec/Type) and (P/IndSpec/Constr) are very similar to the one for (P/RewSpec/Fun).

We are left with conversion rules. The proofs for (P/Conv/Refl), (P/Conv/Sym), (P/Conv/Trans), (P/Conv/Beta), (P/Conv/Path), (P/Conv/App) are easy applications of induction hypotheses. In (P/Conv/Refl) and (P/Conv/Path) it is important that we have to do with module variables, which are not affected by the term substitution.

For (P/Conv/Prod)

\frac{\begin{matrix}E_{1}; E_{2}; E_{3} ⊢_{P} (v : t_{1})t_{2} : t  E_{1}; E_{2}; E_{3} ⊢_{P} (v : t′_{1})t′_{2} : t′ \\ E_{1}; E_{2}; E_{3} ⊢_{P} t_{1} ≈ t′_{1}  E_{1}; E_{2}; E_{3}; v : Ty(t_{1}) ⊢_{P} t_{2} ≈ t′_{2} \end{matrix}}{E_{1}; E_{2}; E_{3} ⊢_{P} (v : t_{1})t_{2} ≈ (v : t′_{1})t′_{2}}

by induction hypothesis we know that E_{1}; E_{3}δ ⊢_{P} (v : t_{1}δ)t_{2}δ : tδ, E_{1}; E_{3}δ ⊢_{P} (v : t′_{1}δ)t′_{2}δ : t′δ and E_{1}; E_{3}δ ⊢_{P} t_{1}δ ≈ t′_{1}δ. By Lemma 5.7.3 E_{1}; E_{3}δ ⊢_{P} t_{1}δ : s for some sort s. Then also E_{1}; E_{3}δ ⊢ t_{1}δ : s holds and by (Ty/Form) E_{1}; E_{3}δ ⊢ Ty(t_{1}δ) : spec. By (Env/Insert) E_{1}; E_{3}δ; v : Ty(t_{1}δ) ⊢ ok. Hence, by induction hypothesis E_{1}; E_{3}δ; v : Ty(t_{1}δ) ⊢_{P} t_{2}δ ≈ t′_{2}δ and by (P/Conv/Prod) E_{1}; E_{3}δ ⊢_{P} (v : t_{1}δ)t_{2}δ ≈ (v : t_{1}δ)t′_{2}δ.

For (P/Conv/Abs) the proof is very similar to the one presented above, with one little exception that we use Lemma 5.7.4 and not 5.7.3 to show that the extended environment is correct.

Lemma 5.7.6. If E_{1}; v : Ty(t); E_{2} ⊢_{P} J, E_{1} ⊢_{P} t ≈ t′, E_{1} ⊢_{P} t : s and E_{1} ⊢_{P} t′ : s′ for some sorts s and s′, then E_{1}; v : Ty(t′); E_{2} ⊢_{P} J.

Proof. By Lemma 5.7.2, E_{1}; v′ : Ty(t′); v : Ty(t); E_{2} ⊢_{P} J, where v′ is a fresh variable. By Lemma 5.7.2 we have also E_{1}; v′ : Ty(t′) ⊢_{P} t ≈ t′ and using (P/Env/Lookup) and (P/Ty/Type) we have E_{1}; v′ : Ty(t′) ⊢_{P} v′ : t′. We can use conversion to obtain E_{1}; v′ : Ty(t′) ⊢_{P} v′ : t. Using Lemma 5.7.1 and specification satisfaction, we get E_{1}; v′ : Ty(t′) ⊢ v′ : Ty(t). By Lemma 4.1.5 we get E_{1}; v′ : Ty(t′); E_{2}{v ↦ v′} ⊢ ok. Now, by Lemma 5.7.5, E_{1}; v′ : Ty(t′); E_{2}{v ↦ v′} ⊢_{P} J{v ↦ v′}. Since v′ was fresh, the latter judgment is α-convertible with the desired conclusion.

5.8 Completeness of principal derivations

The goal of this section is to prove that all term judgments derivable in ⊢ are also derivable in ⊢_{P}. We will do the proof by induction on the derivation in ⊢ and therefore we need a very strong induction hypothesis about term information carried by non-term judgments. Therefore we define a function Essence_{P} (·), which assigns a set of term or path judgments in ⊢_{P} to every judgment in ⊢. Informally Essence_{P} (E ⊢ J) is a complete information about paths and terms that can be deduced from E ⊢ J and expressed with principal judgments.

Before giving the definition of Essence_{P} (·) let us introduce an auxiliary measure function, called weight, which justifies the correctness of the definition of Essence_{P} (·).

Definition 5.8.1. The weight of a term, block type, module type or specification is the number of all Sig ... End’s, Ty()’s and Eq()’s occurring in it.

It is obvious that the weight is invariant under path substitution and that it is smaller (sometimes strictly) in a subexpression.

To see that the definition of Essence_{P} (·) is correct it is enough to remark, that for all judgments J of the form ψ : Φ the recursive call is done with Φ′ of smaller weight.

Definition 5.8.2. The principal essence of a judgment E ⊢ J, written Essence_{P} (E ⊢ J) is the set of principal judgments of the form E; E′ ⊢_{P} J′ defined as follows:

Essence_{P} (E ⊢ e : t) = {E ⊢_{P} e : t}

Essence_{P} (E ⊢ e_{1} ≈ e_{2}) = {E ⊢_{P} e_{1} ≈ e_{2}}

Essence_{P} (E ⊢ p : Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End) = ⋃\{Essence_{P} (E ⊢ \overline{p.v_{i}} : S_{i}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\}) for i = 1 … n\}

Essence_{P} (E ⊢ p_{1} ≈ p_{2}) = {E ⊢_{P} p_{1} ≈ p_{2}}

Essence_{P} (E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End <: Sig \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} … \overline{v′}_{m}▷\overline{v′}_{m} : S′_{m} End) = ⋃\{Essence_{P} (E; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ \overline{v}′_{i} : S′_{i}) for i = 1 … m\} where \{\overline{v}_{1} … \overline{v}_{n}\} ⊇ \{\overline{v}′_{1} … \overline{v}′_{m}\} and v_{i}^{j} = v′_{i′}^{j′} implies v_{i}^{j} = v′_{i′}^{j′} Essence_{P} (E ⊢ IndSpec(E^{I} := E^{C}) : blocktype) =

{E ⊢_{P} t_{j} : s_{j} for j = 1 … k} ∪ {E; E^{I} ⊢_{P} t̂_{i} : ŝ_{i} for i = 1 … n} where E^{I} = v_{1}^{I} : Ty(t_{1}) … v_{k}^{I} : Ty(t_{k}) E^{C} = v_{1}^{C} : Ty(t̂_{1}) … v_{n}^{C} : Ty(t̂_{n})

Essence_{P} (E ⊢ \overline{p} : IndSpec(E^{I} := E^{C}))

{E ⊢_{P} p^{j} : t_{j} for j = 1 … k} ∪ {E ⊢_{P} p^{k+i} : t̂_{i}{v_{j}^{I} ↦ p^{j}} for i = 1 … n} where E^{I} = v_{1}^{I} : Ty(t_{1}) … v_{k}^{I} : Ty(t_{k}) E^{C} = v_{1}^{C} : Ty(t̂_{1}) … v_{n}^{C} : Ty(t̂_{n})

Essence_{P} (E ⊢ IndSpec(E^{I} := E^{C}) ≈ IndSpec(E^{I}′ := E^{C}′)) = {E ⊢_{P} t_{j} ≈ t′_{j} for j = 1 … k} ∪ {E; E^{I} ⊢_{P} t̂_{i} ≈ t̂′_{i} for i = 1 … n}

where E^{I} = v_{1}^{I} : Ty(t_{1}) … v_{k}^{I} : Ty(t_{k}) E^{I}′ = v_{1}^{I} : Ty(t′_{1}) … v_{k}^{I} : Ty(t′_{k})

E^{C} = v_{1}^{C} : Ty(t̂_{1}) … v_{n}^{C} : Ty(t̂_{n}) E^{C}′ = v_{1}^{C} : Ty(t̂′_{1}) … v_{n}^{C} : Ty(t̂′_{n})

Essence_{P} (E ⊢ RewSpec(E^{Γ}, R) : blocktype) = {E ⊢_{P} t_{j} : s_{j} for j = 1 … n} ∪ {E; v_{1}^{i} : Ty(t_{1}^{i}) … v_{j−1}^{i} : Ty(t_{j−1}^{i}) ⊢_{P} t_{j}^{i} : s_{j}^{i} for j = 1 … n_{i}} ∪ {E; E^{Γ}; E_{i} ⊢_{P} t̂_{i} : ŝ_{i} for i = 1 … m} ∪ {E; E^{Γ}; E_{i} ⊢_{P} e_{i}^{L} : t̂_{i} for i = 1 … m} ∪ {E; E^{Γ}; E_{i} ⊢_{P} e_{i}^{R} : t̂_{i} for i = 1 … m} where

E^{Γ} = v_{1} : Ty(t_{1}) … v_{n} : Ty(t_{n})
R = {E_{i} | e_{i}^{L} ⟶ e_{i}^{R} : t̂_{i}}_{i=1...m}
E_{i} = v_{1}^{i} : Ty(t_{1}^{i}) … v_{n_{i}}^{i} : Ty(t_{n_{i}}^{i})

Essence_{P} (E ⊢ \overline{p} : RewSpec(E^{Γ}, R)) = {E ⊢_{P} p^{j} : t_{j} for j = 1 … n} ∪ {E; E_{i} ⊢_{P} e_{i}^{L}{v_{j} ↦ p^{j}} ≈ e_{i}^{R}{v_{j} ↦ p^{j}} for i = 1 … m} where E^{Γ} = v_{1} : Ty(t_{1}) … v_{n} : Ty(t_{n}) R = {E_{i} | e_{i}^{L} ⟶ e_{i}^{R} : t̂_{i}}_{i=1...m}

Essence_{P} (E ⊢ RewSpec(E^{Γ}, R) ≈ RewSpec(E^{Γ}′, R′)) = {E ⊢_{P} t_{j} ≈ t′_{j} for j = 1 … n} ∪ {E; v_{1}^{i} : Ty(t_{1}^{i}) … v_{j−1}^{i} : Ty(t_{j−1}^{i}) ⊢_{P} t_{j}^{i} ≈ t_{j}^{i}′ for i = 1 … m for j = 1 … n_{i}} ∪ {E; E^{Γ}; E_{i} ⊢_{P} e_{i}^{L} ≈ e_{i}^{L}′ for i = 1 … m} ∪ {E; E^{Γ}; E_{i} ⊢_{P} e_{i}^{R} ≈ e_{i}^{R}′ for i = 1 … m} ∪ {E; E^{Γ}; E_{i} ⊢_{P} t̂_{i} ≈ t̂_{i}′ for i = 1 … m} where

E^{Γ} = v_{1} : Ty(t_{1}) … v_{n} : Ty(t_{n}) E^{Γ}′ = v_{1} : Ty(t′_{1}) … v_{n} : Ty(t′_{n})
R = {E_{i} | e_{i}^{L} ⟶ e_{i}^{R} : t̂_{i}}_{i=1...m} R′ = {E′_{i} | e_{i}^{L}′ ⟶ e_{i}^{R}′ : t̂_{i}′}_{i=1...m}
E_{i} = v_{1}^{i} : Ty(t_{1}^{i}) … v_{n_{i}}^{i} : Ty(t_{n_{i}}^{i}) E′_{i} = v_{1}^{i} : Ty(t_{1}^{i}′) … v_{n_{i}}^{i} : Ty(t_{n_{i}}^{i}′)

Essence_{P} (E ⊢ \overline{p} : Ty(Ψ)) = Essence_{P} (E ⊢ \overline{p} : Ψ)

Essence_{P} (E ⊢ \overline{p} : Eq(\overline{ϕ} : Ψ)) = \{E ⊢_{P} p^{i} ≈ ϕ^{i} for all i\} ∪ Essence_{P} (E ⊢ \overline{p} : Ψ)

Essence_{P} (E ⊢ J) = ∅ otherwise Lemma 5.8.1. For any judgment E ⊢ J, Essence_{P} (E ⊢ J) is a finite set of judgments of the form E; E′ ⊢_{P} J′, where E′ is a term environment, unless J is a subtyping judgment. Moreover the function Essence_{P} (·) is compatible with path substitutions, i.e. for a judgment E; \overline{v} : S; E_{1} ⊢ J and a substitution δ = \{\overline{v} ↦ \overline{p}\}

Essence_{P} (E; E_{1}δ ⊢ Jδ) = \{E; E_{1}δ; E′δ ⊢_{P} J′δ | E; \overline{v} : S; E_{1}; E′ ⊢_{P} J′ ∈ Essence_{P} (E; \overline{v} : S; E_{1} ⊢ J)\}

Proof. By induction on the weight of Ψ in case J = ϕ : Ψ and by inspection of the definition of Essence_{P} (·).

The final result of this section, Lemma 5.8.7, will be a proof by induction on the derivation of E ⊢ J that all judgments in Essence_{P} (E ⊢ J) are satisfied. However, some of the typing rules of ⊢ require a more involved proof. These are (Env/Lookup), (Subsumption) and (Strenghtenning). The following lemma is needed to handle the rule (Env/Lookup).

Lemma 5.8.2. For all judgments E ⊢_{P} J, if E ⊢_{P} J holds then every judgment in Essence_{P} (E ⊢ J) is satisfied.

Proof. Let us first consider judgments of the form ϕ : Ψ. Since the definition of Essence_{P} () follows elimination and computation rules, it is quite easy to see that the lemma is true for all such judgments, by simple induction on the weight of Ψ.

The only non-obvious judgment is E ⊢_{P} \overline{p} : RewSpec(E^{Γ}, R). First, using elimination, we obtain E ⊢_{P} p_{j} : t_{j} for all j = 1 … n. Second, for every i = 1 … m, using Lemma 5.7.2, we derive E; E_{i} ⊢_{P} \overline{p} : RewSpec(E^{Γ}, R) and E; E_{i} ⊢_{P} e_{i}^{L}\{v_{j} ↦ p_{j}\} ≈ e_{i}^{R}\{v_{j} ↦ p_{j}\} using the identity substitution in the computation rule.

For judgments of the form e_{1} ≈ e_{2} the lemma obviously holds.

Lemma 5.8.3. For every environment E, a path p and Ψ, which is a module type or term, if E ⊢ p : Ψ then E ⊢_{P} p ≈ p.

Proof. Let p = v.v_{1} ….v_{n}. We will show by induction on n that the lemma holds. Note that by Lemma 5.6.6 we have E ⊢_{P} p : Ψ′ for some Ψ′.

For n = 0, if v is a module variable then E ⊢_{P} v ≈ v follows from (P/Conv/Refl). If v is a term variable then E ⊢_{P} v ≈ v follows from (P/Conv/Beta).

Suppose now that p = p′.v_{n+1}. From E ⊢ p : Ψ it follows by Lemma 4.1.3 that E ⊢ p′ : Ψ̂ for some Ψ̂. Hence, by induction hypothesis we get E ⊢_{P} p′ ≈ p′. Recall that we already have E ⊢_{P} p′.v_{n+1} : Ψ′. Consequently, we can use (P/Conv/Path) to obtain E ⊢ p′.v_{n+1} ≈ p′.v_{n+1}, which is the desired conclusion.

The next two lemmas are needed in the proof of Lemma 5.8.7 to handle the (Subsumption) rule for signatures. Lemma 5.8.5 states the required substitutivity property for Essence_{P} (·). However, since Essence_{P} (·) contains judgments in ⊢_{P}, we first prove by induction on the derivation of E ⊢_{P} J that Essence_{P} (E ⊢ J) for such judgments is closed by substitution.

Lemma 5.8.4. Let E and E_{1} be environments. If E; \overline{v} : S; E_{1} ⊢_{P} J and E ⊢ \overline{p} : S and all judgments in Essence_{P} (E ⊢ \overline{p} : S) hold then all judgments in Essence_{P} (E; E_{1}δ ⊢ Jδ) hold, where δ = \{\overline{v} ↦ \overline{p}\}.

Proof. The proof is done by induction on the derivation of E; \overline{v} : S; E_{1} ⊢_{P} J.

Let us first note that E; \overline{v} : S; E_{1} ⊢_{P} J implies E; \overline{v} : S; E_{1} ⊢ J (by Lemma 5.7.1) and E; \overline{v} : S; E_{1} ⊢ ok (by Lemma 4.1.2). Consequently, by Lemma 4.1.5 we have E; E_{1}δ ⊢ ok.

If the last rule in the derivation of E; \overline{v} : S; E_{1} ⊢_{P} J is (P/Env/Lookup)

\frac{E; \overline{v} : S; E_{1} ⊢ ok}{E; \overline{v} : S; E_{1}; ⊢_{P} \overline{v}′ : S′}

then there are two possibilities: either \overline{v} = \overline{v}′ or \overline{v} ≠ \overline{v}′. If \overline{v}′ are different from \overline{v} then we can simply use (P/Env/Lookup) on E; E_{1}δ ⊢ ok to get E; E_{1}δ ⊢_{P} \overline{v}′δ : S′δ if \overline{v}′ : S′ ∈ E_{1} or E; E_{1}δ ⊢_{P} \overline{v}′ : S′ otherwise, and use Lemma 5.8.2 to get the desired conclusion.

If \overline{v}′ = \overline{v} then we can weaken the judgments in Essence_{P} (E ⊢ \overline{p} : S) by Lemma 5.7.2, to get the judgments in Essence_{P} (E; E_{1}δ ⊢ \overline{p} : S), which is what we wanted since \overline{v}δ = \overline{p}.

In case of the rule (P/Sig/Access):

E; \overline{v} : S; E_{1} ⊢_{P} p′ : Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End
E; \overline{v} : S; E_{1}; ⊢_{P} \overline{p′.v_{i}} : S_{i}\{\overline{v}_{j} ↦ \overline{p′.v_{j}}\}

we know that judgments in Essence_{P} (E; E_{1}δ ⊢ p′δ : (Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End)δ) are valid by induction hypothesis. By definition of Essence_{P} (·) the latter set is a superset of Essence_{P} (E; E_{1}δ ⊢ \overline{p′δ.v_{i}} : S_{i}δ\{\overline{v}_{j} ↦ \overline{p′δ.v_{j}}\}). Fortunately, since p′δ.v_{j} = p′.v_{j}δ, we can write the latter set as Essence_{P} (E; E_{1}δ ⊢ \overline{p′.v_{i}}δ : S_{i}\{\overline{v}_{j} ↦ \overline{p′.v_{j}}\}δ) which is the desired conclusion.

For (P/Ty/Type) the lemma trivially holds, since Essence_{P} (E; E_{1}δ ⊢ \overline{p} : Ψ) = Essence_{P} (E; E_{1}δ ⊢ \overline{p} : Ty(Ψ)) which is true by induction hypothesis.

For (P/Eq/Type) and (P/Eq/Comp) the proof is similar since Essence_{P} (E; E_{1}δ ⊢ \overline{p} : Ψ) and all Essence_{P} (E; E_{1}δ ⊢ p^{i} ≈ ϕ^{i}) are included in Essence_{P} (E; E_{1}δ ⊢ \overline{p} : Eq(ϕ : Ψ)).

For almost all term rules, the lemma holds by simply using induction hypothesis. An exception is the rule (P/Term/Axiom) whose premise does not use principal typing ⊢_{P}. But we know already that E; E_{1}δ ⊢ ok and hence by (P/Term/Axiom) we get E; E_{1}δ ⊢_{P} s_{1} : s_{2} which is the desired conclusion.

If the last rule in the derivation is (P/RewSpec/Fun) then the conclusion follows easily by induction hypothesis. Let us now consider the rule (P/RewSpec/Comp):

\frac{E; \overline{v} : S; E_{1} ⊢_{P} \overline{p}′ : RewSpec(E^{Γ}, R) ξ : Ê_{i} → E; \overline{v} : S; E_{1}}{E; \overline{v} : S; E_{1} ⊢_{P} e_{i}^{L}θξ ≈ e_{i}^{R}θξ} where θ = \{v′_{j} ↦ p^{j}′\}_{j=1...n}

where E^{Γ} = v′_{1} : Ty(t_{1}) … v′_{n} : Ty(t_{n}) and R = {Ê_{i} | e_{i}^{L} ⟶ e_{i}^{R} : t̂_{i}}_{i=1...m}. By induction hypothesis, all judgments in Essence_{P} (E; E_{1}δ ⊢ \overline{p}′δ : RewSpec(E^{Γ}δ, Rδ)) hold. In particular we have E; E_{1}δ; Ê_{i}δ ⊢_{P} e_{i}^{L}{v′_{j} ↦ p′^{j}}δ ≈ e_{i}^{R}{v′_{j} ↦ p′^{j}}δ.

From the induction hypothesis about ξ : Ê_{i} → E; \overline{v} : S; E_{1}, we get ξδ : Ê_{i}δ → E; E_{1}δ i.e. E; E_{1}δ ⊢_{P} v_{k}^{i}ξδ : t_{k}^{i}δ for all k = 1...n_{i} (where Ê_{i} = v_{1}^{i} : Ty(t_{1}^{i}) … v_{n_{i}}^{i} : Ty(t_{n_{i}}^{i}) is a term environment).

Thus, using Lemma 5.7.5, we get E; E_{1}δ ⊢_{P} e_{i}^{L}{v′_{j} ↦ p′^{j}}δ(ξδ) ≈ e_{i}^{R}{v′_{j} ↦ p′^{j}}δ(ξδ). Since the domain of ξ and co-domain of δ are disjoint, the above is equivalent to

E; E_{1}δ ⊢_{P} e_{i}^{L}{v′_{j} ↦ p′^{j}}ξδ ≈ e_{i}^{R}{v′_{j} ↦ p′^{j}}ξδ

which is exactly what we have to prove.

For (P/IndSpec/IndType) and (P/IndSpec/Constr) the lemma easily holds by induction hypothesis.

Concerning conversion rules the only nontrivial proofs are for (P/Conv/Refl) and (P/Conv/Path). For (P/Conv/Refl)

\frac{E; \overline{v} : S; E_{1} ⊢_{P} v′ : M}{E; \overline{v} : S; E_{1} ⊢_{P} v′ ≈ v′}

v′ is a module variable and either v′ = v or v′ ∉ \overline{v}′. In the latter case from E; \overline{v} : S; E_{1} ⊢ v′ : M we conclude E; E_{1}δ ⊢ v′ : Mδ, by Lemma 4.1.5, and E; E_{1}δ ⊢_{P} v′ : M′ for some M′, by Lemma 5.6.5. Hence, by (P/Conv/Refl) E; E_{1}δ ⊢ v′ ≈ v′. In the former case we have to show that E; E_{1}δ ⊢_{P} p ≈ p where p satisfies E ⊢ p : S by assumption of the lemma. But from Lemma 5.8.3 we have already E ⊢_{P} p ≈ p and by Lemma 5.7.2 E; E_{1}δ ⊢_{P} p ≈ p.

For (P/Conv/Path)

\frac{E; \overline{v} : S; E_{1} ⊢_{P} p′.v : Ψ E; \overline{v} : S; E_{1} ⊢_{P} p′ ≈ p′′ E; \overline{v} : S; E_{1} ⊢_{P} p′′.v : Ψ′}{E; \overline{v} : S; E_{1} ⊢_{P} p.v ≈ p′.v}

we want to show that E; E_{1}δ ⊢_{P} (p′.v)δ ≈ (p′′.v)δ follows from Essence_{P} (E; E_{1}δ ⊢ (p′.v)δ : Ψδ), E; E_{1}δ ⊢_{P} p′δ ≈ p′′δ and Essence_{P} (E; E_{1}δ ⊢ (p′′.v)δ : Φ′δ). If p′.v and p′′.v are terms then Essence_{P} (E; E_{1}δ ⊢ (p′.v)δ : Ψδ) = {E; E_{1}δ ⊢_{P} (p′.v)δ : Ψδ} and Essence_{P} (E; E_{1}δ ⊢ (p′′.v)δ : Ψ′δ) = {E; E_{1}δ ⊢_{P} (p′′.v)δ : Ψ′δ} and we can use (P/Conv/Path) to get E; E_{1}δ ⊢_{P} (p′.v)δ ≈ (p′′.v)δ

For module paths, the premise E; \overline{v} : S; E_{1} ⊢_{P} p′.v : Ψ implies E; \overline{v} : S; E_{1} ⊢ p′.v : Ψ and E; E_{1}δ ⊢ (p′.v)δ : Ψδ (by Lemma 4.1.5). Hence by Lemma 5.6.5 E; E_{1}δ ⊢_{P} (p′.v)δ : Ψ̂ for some Ψ̂. Similarly, we have E; E_{1}δ ⊢_{P} (p′′.v)δ : Ψ̂ for some Ψ̂′ and we can use (P/Conv/Path) to get the desired conclusion.

Lemma 5.8.5. Let E and E_{1} be environments and let E; \overline{v} : S; E_{1} ⊢ J and E ⊢ \overline{p} : S. If all judgments in Essence_{P} (E; \overline{v} : S; E_{1} ⊢ J) and Essence_{P} (E ⊢ \overline{p} : S) hold then all judgments in Essence_{P} (E; E_{1}δ ⊢ Jδ) hold, where δ = \{\overline{v} ↦ \overline{p}\}.

Proof. By Lemma 5.8.1, we just have to show the validity of E; E_{1}δ; E′δ ⊢_{P} J′δ for all judgments E; \overline{v} : S; E_{1}; E′ ⊢_{P} J′ ∈ Essence_{P} (E; \overline{v} : S; E_{1} ⊢ J).

By Lemma 5.8.4, all judgments in Essence_{P} (E; E_{1}δ; E′δ ⊢ J′δ) hold and since J′ is a term judgment, Essence_{P} (E; E_{1}δ; E′δ ⊢ J′δ) = {E; E_{1}δ; E′δ ⊢_{P} J′δ} and we are done.

The following lemma is needed to handle the rule (Strenghtenning) in the proof of Lemma 5.8.7.

Lemma 5.8.6. For every module type M and specification S the following implications hold:

• if E ⊢ p : M and all judgments in Essence_{P} (E ⊢ p : M) are valid then judgments in Essence_{P} (E ⊢ p : M/p) are also valid,

• if E ⊢ \overline{p} : S and all judgments in Essence_{P} (E ⊢ \overline{p} : S) are valid then judgments in Essence_{P} (E ⊢ \overline{p} : S/\overline{p}) are also valid.

Proof. By induction on the weight of M and S respectively. In case M is a functor type, Essence_{P} (E ⊢ p : M/p) is empty.

Otherwise M is a signature Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End, and Essence_{P} (E ⊢ p : M/p) is a sum of sets Essence_{P} (E ⊢ \overline{p.v_{i}} : S_{i}/\overline{p.v_{i}}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\}) for i = 1 … n. Since S_{i}/\overline{p.v_{i}}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\} = S_{i}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\}/\overline{p.v_{i}} judgments in all these sets are valid by induction hypothesis.

If S is an abstract specification Ty(Ψ) then S/\overline{p} is Eq(\overline{p} : Ψ/\overline{p}). In that case Essence_{P} (E ⊢ \overline{p} : S/\overline{p}) is equal \{E ⊢_{P} \overline{p} ≈ \overline{p}\} ∪ Essence_{P} (E ⊢ \overline{p} : Ψ/\overline{p}). The first part is true by Lemma 5.8.3 and the second is either equivalent to the assumption or follows from the induction hypothesis in case Ψ is a module type M.

If S is a manifest specification Eq(ϕ : Ψ) the reasoning is very similar.

Lemma 5.8.7. If E ⊢ J then every judgment in Essence_{P} (E ⊢ J) is satisfied. In particular if E ⊢ e : t then E ⊢_{P} e : t.

Proof. By induction on the derivation of E ⊢ J.

If the last rule in the derivation of E ⊢ J is one of the following: (Env/Empty), (Env/Insert), (Sig/Form), (Sig/Struct), (Funsig/Form), (Funsig/Functor), (Funsig/Sub), (Mod/App), (Ty/Form), (Eq/Form), (IndSpec/Ind), (RewSpec/Rew), (Conv/Ty), (Conv/Eq), (Conv/Sig) or (Conv/Funsig), then Essence_{P} (E ⊢ J) = ∅ and the lemma trivially holds. Let us examine other rules.

Since (Env/Lookup) is also present in ⊢_{P} (under the name (P/Env/Lookup)), we can use it to obtain E ⊢_{P} \overline{v} : S, and the satisfaction of all judgments from Essence_{P} (E ⊢ \overline{v} : S) follows from Lemma 5.8.2.

For (Term/Prod), by induction hypothesis we have Essence_{P} (E ⊢ t_{1} : s_{1}) = {E ⊢_{P} t_{1} : s_{1}} and Essence_{P} (E; v : Ty(t_{1}) ⊢ t_{2} : s_{1}) = {E; v : Ty(t_{1}) ⊢_{P} t_{2} : s_{2}}. Hence, we can use (P/Term/Prod) and get E ⊢_{P} (v : t_{1})t_{2} : s_{3} which is the only element of the desired conclusion Essence_{P} (E ⊢ (v : t_{1})t_{2} : s_{3}). The proofs for other term rules are very similar to the one presented above.

For (Sig/Access) we want to know that all judgments in Essence_{P} (E ⊢ \overline{p.v_{k}} : S_{k}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\}) hold. But this set is included in Essence_{P} (E ⊢ p : Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End) whose elements hold by induction hypothesis.

The case of (Sig/Sub) is even simpler, since the essence of the premise and of the conclusion of the rule are equal.

Let us now consider the rule (Subsumption). If the module types appearing in this rule are functor types or a module expression is not a path, then the essence of the conclusion is empty. Otherwise the module expression is a path p and the module types are two signatures:

\frac{E ⊢ p : Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End <: Sig \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} … \overline{v′}_{m}▷\overline{v′}_{m} : S′_{m} End}{E ⊢ p : Sig \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} … \overline{v′}_{m}▷\overline{v′}_{m} : S′_{m} End}

From the induction hypothesis about the first premise we have the validity of all judgments from Essence_{P} (E ⊢ p : Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End) which is equal to the sum of the sets Essence_{P} (E ⊢ \overline{p.v_{i}} : S_{i}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\}) for all i = 1 … n.

From the induction hypothesis about the subtyping judgment, we have the validity of Essence_{P} (E; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ \overline{v}′_{i} : S′_{i}) for all i = 1 … m. Since E ⊢ p.v_{1} : S_{1} and all judgments from Essence_{P} (E ⊢ p.v_{1} : S_{1}) are valid, using lemma 5.8.5, we can get any judgment in Essence_{P} (E; \overline{v}_{2} : S_{2}\{\overline{v}_{1} ↦ \overline{p.v_{1}}\} … \overline{v}_{n} : S_{n}\{\overline{v}_{1} ↦ \overline{p.v_{1}}\} ⊢ \overline{v}′_{i}\{\overline{v}_{1} ↦ \overline{p.v_{1}}\} : S′_{i}\{\overline{v}_{1} ↦ \overline{p.v_{1}}\}). We can continue substituting \overline{p.v_{j}} to \overline{v}_{j} until j = n and we obtain the validity of all judgments in Essence_{P} (E ⊢ \overline{p.v′_{i}} : S′_{i}\{\overline{v}′_{j} ↦ \overline{p.v′_{j}}\}) for any i, because \overline{v}′_{i}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\} = \overline{p.v′_{i}} and S′_{i}\{\overline{v}′_{j} ↦ \overline{p.v′_{j}}\} = S′_{i}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\}. Since the desired conclusion Essence_{P} (E ⊢ p : Sig \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} … \overline{v′}_{m}▷\overline{v′}_{m} : S′_{m} End) is equal to the sum of Essence_{P} (E ⊢ \overline{p.v′_{i}} : S′_{i}\{\overline{v}′_{j} ↦ \overline{p.v′_{j}}\}) for all i = 1 … m, we are done.

For the (Strengthening) rule, we simply apply Lemma 5.8.6.

For (Ty/Sat), (Ty/Type), (Eq/Sat), (Eq/Type) and (Eq/Comp) it is easy to see that the essence of the conclusion is a subset of the essence of the premises. The same happens for (IndSpec/Form), (IndSpec/IndType), (IndSpec/Constr), (RewSpec/Form), (RewSpec/Fun) and (RewSpec/Sat).

For (RewSpec/Comp) we want to show that E ⊢_{P} e_{i}^{L}θδ ≈ e_{i}^{R}θδ holds. By induction hypothesis we know that E; E_{i} ⊢_{P} e_{i}^{L}θ ≈ e_{i}^{R}θ. But δ : E_{i} → E is a term substitution and we can use Lemma 5.7.5 to get the desired conclusion.

For (Block/Conv), there are two cases, for IndSpec and RewSpec. We will examine the more difficult case for RewSpec. From induction hypothesis, we have the validity of the following judgments:

Essence_{P} (E ⊢ \overline{p} : RewSpec(E^{Γ}, R)):

1. {E ⊢_{P} p^{j} : t_{j} for j = 1 … n}

2. {E; E_{i} ⊢_{P} e_{i}^{L}{v_{j} ↦ p^{j}} ≈ e_{i}^{R}{v_{j} ↦ p^{j}} for i = 1 … m}

Essence_{P} (E ⊢ RewSpec(E^{Γ}, R) ≈ RewSpec(E^{Γ}′, R′)):

3. {E ⊢_{P} t_{j} ≈ t′_{j} for j = 1 … n} 4. {E; v_{1}^{i} : Ty(t_{1}^{i}) … v_{j−1}^{i} : Ty(t_{j−1}^{i}) ⊢_{P} t_{j}^{i} ≈ t_{j}^{i}′ for i = 1 … m for j = 1 … n_{i}}

5. {E; E^{Γ}; E_{i} ⊢_{P} e_{i}^{L} ≈ e_{i}^{L}′ for i = 1 … m}

6. {E; E^{Γ}; E_{i} ⊢_{P} e_{i}^{R} ≈ e_{i}^{R}′ for i = 1 … m}

7. {E; E^{Γ}; E_{i} ⊢_{P} t̂_{i} ≈ t̂_{i}′ for i = 1 … m}

Essence_{P} (E ⊢ RewSpec(E^{Γ}′, R′) : blocktype):

8. {E ⊢_{P} t′_{j} : s′_{j} for j = 1 … n}

9. {E; v_{1}^{i} : Ty(t_{1}^{i}′) … v_{j−1}^{i} : Ty(t_{j−1}^{i}′) ⊢_{P} t_{j}^{i}′ : s_{j}^{i}′ for j = 1 … n_{i}}

10. {E; E^{Γ}′; E_{i}′ ⊢_{P} t̂_{i}′ : ŝ_{i}′ for i = 1 … m}

11. {E; E^{Γ}′; E_{i}′ ⊢_{P} e_{i}^{L}′ : t̂_{i}′ for i = 1 … m}

12. {E; E^{Γ}′; E_{i}′ ⊢_{P} e_{i}^{R}′ : t̂_{i}′ for i = 1 … m}

where

E^{Γ} = v_{1} : Ty(t_{1}) … v_{n} : Ty(t_{n}) E^{Γ}′ = v_{1} : Ty(t′_{1}) … v_{n} : Ty(t′_{n})
R = {E_{i} | e_{i}^{L} ⟶ e_{i}^{R} : t̂_{i}}_{i=1...m} R′ = {E′_{i} | e_{i}^{L}′ ⟶ e_{i}^{R}′ : t̂_{i}′}_{i=1...m}
E_{i} = v_{1}^{i} : Ty(t_{1}^{i}) … v_{n_{i}}^{i} : Ty(t_{n_{i}}^{i}) E′_{i} = v_{1}^{i} : Ty(t_{1}^{i}′) … v_{n_{i}}^{i} : Ty(t_{n_{i}}^{i}′)

Our goal is the validity of judgments in Essence_{P} (E ⊢ \overline{p} : RewSpec(E^{Γ}′, R′)), i.e.

{E ⊢_{P} p^{j} : t′_{j} for j = 1 … n} ∪ {E; E′_{i} ⊢_{P} e_{i}^{L}′{v_{j} ↦ p^{j}} ≈ e_{i}^{R}′{v_{j} ↦ p^{j}} for i = 1 … m}

The validity of the judgments from the first set can easily be obtained using judgments 1, 3 and 8 and the conversion rule.

Let us now consider the second set of judgments. First, from hypothesis 5 and 6 and Lemma 5.7.5, we obtain E; E_{i} ⊢_{P} e_{i}^{L}{v_{j} ↦ p^{j}} ≈ e_{i}^{L}′{v_{j} ↦ p^{j}} and E; E_{i} ⊢_{P} e_{i}^{R}{v_{j} ↦ p^{j}} ≈ e_{i}^{R}′{v_{j} ↦ p^{j}}. Using transitivity, symmetry and hypothesis 2, we can show that E; E_{i} ⊢_{P} e_{i}^{L}′{v_{j} ↦ p^{j}} ≈ e_{i}^{R}′{v_{j} ↦ p^{j}}. Now it is enough to use Lemma 5.7.6 n_{i} times to get the desired conclusion. Since the above reasoning can be carried on for any i, we are done.

We are left with conversion rules. The only ones for which the proof does not trivially hold by induction hypothesis are (Conv/Refl) and (Conv/Path).

For (Conv/Refl), the premise of the rule is E ⊢ v : M. Since v is module variable, by Lemma 5.6.5 we have E ⊢_{P} v : M′ for some M′. Hence, by (P/Conv/Refl) E ⊢_{P} v ≈ v which is the desired conclusion.

For (Conv/Path)

\frac{E ⊢ p.v : Ψ E ⊢ p ≈ p′ E ⊢ p′.v : Ψ′}{E ⊢ p.v ≈ p′.v}

we have two possibilities. If p.v, p′.v are term paths then by induction hypothesis we have E ⊢_{P} p.v : Ψ, E ⊢_{P} p ≈ p′, E ⊢_{P} p′.v : Ψ′ and by (P/Conv/Path) we get E ⊢_{P} p.v ≈ p′.v, i.e. Essence_{P} (E ⊢ p.v ≈ p′.v). If p.v, p′.v are module paths then by Lemma 5.6.5 we have E ⊢_{P} p.v : Ψ̂, E ⊢_{P} p′.v : Ψ̂′ for some Ψ̂, Ψ̂′ and again by (P/Conv/Path) we get the desired conclusion.

5.9 Module elimination

The following two lemmas are the main goal of this chapter. They are used to prove Lemma 7.2.5 announced in Chapter 3 as Lemma 3.3.1. They both follow the pattern described in the beginning of Section 5.7.

Lemma 5.9.1. Let E = E_{1}; v : Ty(M); E_{2}, where E_{2} does not contain module elements and M = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End. Let E′ = E_{1}; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}; E_{2}δ where δ = \{\overline{v.v_{k}} ↦ \overline{v}_{k}\}. If E′ ⊢ ok and E ⊢_{P} J then E′ ⊢_{P} Jδ unless J is of the form v : Ty(M), v : M or v ≈ v.

Proof. We do the proof by induction on the derivation of E ⊢_{P} J. Let us consider the rule (P/Env/Lookup)

\frac{Ê_{1}; \overline{v}′ : S′; Ê_{2} ⊢ ok}{Ê_{1}; \overline{v}′ : S′; Ê_{2} ⊢_{P} \overline{v}′ : S′}

If \overline{v}′ is different from v, we simply use (P/Env/Lookup) to look up the specification of \overline{v}′ in E′. If \overline{v}′ comes from E_{2} then S′ is substituted in E′ and if it comes from E_{1} then S′ = S′δ, so in both situations we get E′ ⊢_{P} \overline{v}′ : S′δ as a conclusion. Of course \overline{v}′δ = \overline{v}′.

For the rule (P/Sig/Access) the premise concerns either the variable v or some other path p. In the first case the premise is E ⊢_{P} v : M and we have to show that

E′ ⊢_{P} \overline{v.v_{i}}δ : S_{i}\{\overline{v}_{j} ↦ \overline{v.v_{j}}\}δ

But the latter is equivalent to E′ ⊢_{P} \overline{v}_{i} : S_{i} which can be obtained using the rule (P/Env/Lookup).

In case p is a different path, by induction hypothesis we get

E′ ⊢_{P} pδ : Sig \overline{v}′_{1}▷\overline{v}′_{1} : S′_{1}δ … \overline{v}′_{m}▷\overline{v}′_{m} : S′_{m}δ End

Using (P/Sig/Access) we obtain

E′ ⊢_{P} \overline{(pδ).v′_{i}} : S′_{i}δ\{\overline{v}′_{j} ↦ \overline{(pδ).v′_{j}}\}

which is equivalent to

E′ ⊢_{P} \overline{p.v′_{i}}δ : S′_{i}\{\overline{v}′_{j} ↦ p.\overline{v}′_{j}\}δ

because the codomain of δ, the set \{\overline{v}_{1} … \overline{v}_{n}\}, is disjoint with \{\overline{v}′_{1} … \overline{v}′_{m}\}.

The specification rules are easily handled by induction hypothesis.

The term rules are also easily handled with the exception of the rules (P/Term/Prod) and (P/Term/Abs), where the environment in the premise is richer than the one in the conclusion. Let us start by (P/Term/Abs):

\frac{E; v′ : Ty(t_{1}) ⊢_{P} e : t_{2} E ⊢_{P} (v′:t_{1})t_{2} : s}{E ⊢_{P} [v′:t_{1}]e : (v′:t_{1})t_{2}}

Before using the first induction hypothesis, we must make sure E′; v′ : Ty(t_{1}δ) ⊢ ok. However, the second induction hypothesis E′ ⊢_{P} (v′:t_{1}δ)t_{2}δ : s lets us deduce E′ ⊢_{P} t_{1}δ : s_{1} by Lemma 5.7.3. This implies E′ ⊢ t_{1}δ : s_{1} and hence E′ ⊢ Ty(t_{1}δ) : spec and E′; v′ : Ty(t_{1}δ) ⊢ ok by (Ty/Form) and (Env/Insert). Now we can use both induction hypotheses and get the desired conclusion by (P/Term/Abs).

For (P/Term/Prod) we do the same trick, but the sort of the domain type of the substituted product comes directly from the first induction hypothesis.

Block rules and conversion rules all follow from the induction hypothesis except for (P/Conv/Prod) and (P/Conv/Abs) where the richer substituted environment must be proved correct like for the rule (P/Term/Abs), using Lemmas 5.7.3 and 5.7.4 respectively.

Lemma 5.9.2. Let E = E_{1}; v : Ty(M); E_{2}, where E_{2} does not contain module elements and M = Funsig(v̂ : M_{1})M_{2}. If E_{1}; E_{2} ⊢ ok and E ⊢_{P} J then E_{1}; E_{2} ⊢_{P} J unless J is of the form v : Ty(M), v : M or v ≈ v.

Proof. The proof is very similar to the previous one, but simpler.

5.10 Correctness of types

The following lemma completes the correctness of types established by lemma 4.1.15.

Lemma 5.10.1. Let E be an environment, e and t terms. If E ⊢ e : t, then E ⊢ t : s or t = s for some sort s.

Proof. Since by lemma 5.8.7 all term judgments are also derivable in ⊢_{P}, it is sufficient to show that E ⊢_{P} e : t implies E ⊢ t : s or t = s for some sort s. Let us do the proof by induction on the derivation of E ⊢_{P} e : t

If the last rule in the derivation is (P/Eq/Type)

\frac{E ⊢_{P} p : Eq(e : t)}{E ⊢_{P} p : t}

then by Lemma 5.7.1 we have E ⊢ p : Eq(e : t) and by Lemma 4.1.15 E ⊢ Eq(e : t) : spec. Since the judgment of that form can only be obtained by the (Eq/Form) rule, we know that E ⊢ t : s, for some sort s.

The proof for (P/Ty/Type) is very similar.

If the last rule in the derivation is (P/Term/Prod), (P/Term/Abs), (P/Term/Axiom) or (P/Term/Conv) the desired conclusion follows immediately from the premises of these rules. The situation is slightly more complicated for (P/Term/App)

\frac{E ⊢_{P} e : (v : t_{1})t_{2} E ⊢_{P} e′ : t_{1}}{E ⊢_{P} e e′ : t_{2}\{v ↦ e′\}}

By induction hypothesis E ⊢_{P} (v : t_{1})t_{2} : s for some sort s and by Lemma 5.7.3 E; v : Ty(t_{1}) ⊢_{P} t_{2} : s′ for some sort s′. Since E ⊢_{P} e′ : t_{1} we can use Lemma 5.7.5 and get E ⊢_{P} t_{2}{v ↦ e′} : s′.

If the last rule in the derivation is (P/RewSpec/Fun)

\frac{E ⊢_{P} \overline{p} : RewSpec(E^{Γ}, R)}{E ⊢_{P} p^{j} : t_{j}}

then we know that E ⊢ \overline{p} : RewSpec(E^{Γ}, R) and by Lemma 4.1.15 E ⊢ RewSpec(E^{Γ}, R) : blocktype. Since the judgment of that form can be obtained only by (RewSpec/Form) rule, we know that E ⊢ t_{j} : s_{j} for some sort s_{j}.

The proofs for (P/IndSpec/IndType) and (P/IndSpec/Constr) are very similar.

Module evaluation

In this chapter we describe a deterministic procedure evaluating all module applications in a structure. After defining all elements of the evaluation procedure (Section 6.1), we show that it preserves types in Section 6.2 and that it is terminating (Section 6.3). The final result of this chapter, Lemma 6.3.21, easily implies Lemma 3.2.1 from Chapter 3, completing the first part of the conservativity proof.

6.1 Definitions

The evaluation will take place from left to right in a textual representation of a structure. Since we need to establish properties of a structure evaluated up to a certain point, we need to define the notions of prefix and suffix of a structure as well as a set of evaluation positions denoting places where a structure can be cut into a prefix and a suffix.

6.1.1 Structure prefixes and suffixes

Definition 6.1.1. Syntactic classes of prefixes and suffixes of a structure, denoted by Pre and Post respectively, are defined by the following grammar:

Pre ::= ϵ | Pre \overline{v}▷\overline{v} : S := P | Pre \overline{v}▷\overline{v} : S := Struct
Post ::= ϵ | \overline{v}▷\overline{v} : S := P Post | End Post

To mark unbalanced Struct’s in Pre and End’s in Post, we use the underline \underline{Struct} and \underline{End}. As an example, consider the following prefix and suffix:

Pre = v_{1}▷v_{1} : S_{1} := P_{1}; v_{2}▷v_{2} : S_{2} := \underline{Struct} v_{21}▷v_{21} : S_{21} := P_{21}
Post = v_{22}▷v_{22} : S_{22} := P_{22}; v_{23}▷v_{23} : S_{23} := P_{23} \underline{End}; v_{3}▷v_{3} : S_{3} := P_{3}

Now, Struct Pre Post End is the following syntactically correct structure:

Struct

v_{1}▷v_{1} : S_{1} := P_{1}
v_{2}▷v_{2} : S_{2} := Struct

v_{21}▷v_{21} : S_{21} := P_{21}

v_{22}▷v_{22} : S_{22} := P_{22} v_{23}▷v_{23} : S_{23} := P_{23}

End

v_{3}▷v_{3} : S_{3} := P_{3} End

The double horizontal bar denotes a place where the structure is divided into Pre and Post. In the text, the horizontal bar is replaced by the mark || giving Struct Pre || Post End. All possible places where a mark can be located in a structure are called evaluation positions:

Definition 6.1.2. The set of evaluation positions in a structure

m = Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{k}▷\overline{v}_{k} : S_{k} := P_{k} End

denoted by Ev(m), is defined as follows:

• 1, ..., k + 1 are evaluation positions in m,

• if q is an evaluation position in m and

m|_{q} = v▷v : S := Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End

then q · 2 · i for i = 1 … n + 1 are evaluation positions in m.

Note that for positions q′ = q · 2 · i where i ≤ n or q′ = i where i ≤ k, m|_{q′} is a valid component subexpression of m. In other cases, for q′ = q · 2 · n′ with n′ = n + 1 or for q′ = k + 1, m|_{q′} does not denote a valid component subexpression.

We extend the notation BV (m, q′) in the straightforward way to account also for those additional evaluation positions. Given a structure m as in the definition of evaluation positions,

• if q′ = k + 1 then BV (m, q′) = \overline{v}_{1} : S_{1} … \overline{v}_{k} : S_{k},

• if q′ = q · 2 · n′ with n′ = n + 1, then BV (m, q′) = BV (m, q); \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}

By analogy to the definition of evaluated structure components and structures (Definition 3.1.1), we define evaluated structure prefixes.

Definition 6.1.3. A structure prefix Pre is evaluated if all its complete structure components are evaluated.

In other words, no path application can occur in Pre and for every structure component v▷v : Ty(M) := m occurring in Pre, m is evaluated and if it is a structure then M is its primary signature.

6.1.2 Value of a path

In order to evaluate a subexpression corresponding to a module application p_{1} p_{2}, we must determine the value of p_{1}. We search for this value in the context preceding the subexpression p_{1} p_{2}.

Definition 6.1.4. Given an evaluated structure prefix Pre and a module path p, a value of the path p in Pre, denoted val_{p}(Pre) is defined as follows:

• for a variable

val_{v}(Pre v▷v : Ty(M) := m) = m

val_{v}(Pre v▷v : Eq(p : M) := m) = p

val_{v}(Pre \overline{v}′▷\overline{v}′ : S := P) = val_{v}(Pre) if v ≠ \overline{v}′

val_{v}(Pre v′▷v′ : S := \underline{Struct}) = val_{v}(Pre) if v ≠ v′

• for a complex path

val_{p.v_{i}}(Pre) = m_{i}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\}

if val_{p}(Pre) =

Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … v_{i}▷v_{i} : Ty(M) := m_{i} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End

val_{p.v_{i}}(Pre) = p′\{\overline{v}_{j} ↦ \overline{p.v_{j}}\} if val_{p}(Pre) =

Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … v_{i}▷v_{i} : Eq(p′ : M) := m_{i} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End

val_{p.v_{i}}(Pre) = p′′.v_{i} if val_{p}(Pre) = p′′

The following lemma says that if a structure prefix is evaluated, the value of a path is typable by the principal type of the path.

Lemma 6.1.1. Let m = Struct Pre || Post End be a structure well-typed in the empty environment, where Pre is evaluated. Let E = BV (m, q) where q is the position of the mark in m.

If E ⊢_{P} p : M then E ⊢ val_{p}(Pre) : M and val_{p}(Pre) is either a path, a functor or a structure Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End. In the latter case the structure is evaluated and M = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End.

Proof. By induction on the derivation of E ⊢_{P} p : M. The last rule is obviously (P/Ty/Type) or (P/Eq/Type), and before that there is either (P/Env/Lookup) or (P/Sig/Access). In the former case p is a variable and the conclusion is obvious.

In the latter case p = p′.v_{i} with E ⊢_{P} p′.v_{i} : M\{\overline{v}_{j} ↦ \overline{p′.v_{j}}\}. The premise of the (P/Sig/Access) rule is E ⊢_{P} p′ : Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … v_{i}▷v_{i} : S … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End. By induction hypothesis m′ = val_{p′}(Pre) has the following properties:

E ⊢ m′ : Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … v_{i}▷v_{i} : Ty(M) … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End

where S = Ty(M) or Eq(p̂: M) and m′ is either a path p′′, in which case val_{p}(Pre) = p′′.v_{i} and E ⊢ p′′ : M\{\overline{v}_{j} ↦ \overline{p′′.v_{j}}\} or m′ is an evaluated structure

Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … v_{i}▷v_{i} : S := m′′ … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End

In the latter case if S = Ty(M) then val_{p}(Pre) = m′′\{\overline{v}_{j} ↦ \overline{p′.v_{j}}\}. By inversion

E; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ m′′ : M

and given that E ⊢ \overline{p′.v_{k}} : S_{k}\{\overline{v}_{j} ↦ \overline{p′.v_{j}}\}_{j=1...k−1} for all k = 1...n, we can use i − 1 times Lemma 4.1.5, to get

E ⊢ m′′\{\overline{v}_{j} ↦ \overline{p′.v_{j}}\} : M\{\overline{v}_{j} ↦ \overline{p′.v_{j}}\}

Since m′ is evaluated, m′′ is either a path, a functor or an evaluated structure. In the latter case M is its primary signature and hence M\{\overline{v}_{j} ↦ \overline{p′.v_{j}}\} is the primary signature of m′′\{\overline{v}_{j} ↦ \overline{p′.v_{j}}\}.

If S = Eq(p̂: M) then val_{p}(Pre) = p̂\{\overline{v}_{j} ↦ \overline{p′.v_{j}}\}. By correctness of the module type of p′, we have E; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ p̂: M and hence E ⊢ p̂\{\overline{v}_{j} ↦ \overline{p′.v_{j}}\} : M\{\overline{v}_{j} ↦ \overline{p′.v_{j}}\} as before.

6.1.3 The evaluation function

The function Eval(Pre || Post) transforms a structure into an evaluated structure. The transformation is performed by small steps from left to right. The two parts — the evaluated Pre and not evaluated Post — are separated by the mark || located on an evaluation position in Struct Pre Post End. For the sake of uniformity, we omit the outermost Struct and End in the definition of Eval.

There are four kinds of steps in the evaluation. A simple step just moves the mark over a term or block component or a module component which is already evaluated (but is not a structure). A structure start step moves the mark inside a sub-structure and structure end outside a sub-structure, correcting its exported module type. A computation step evaluates a module application using the function val defined in the previous section.

Definition 6.1.5. The evaluation function Eval(Pre || Post) is defined by the following set of rewrite rules

simple steps: q · i ⟶ q · i′, where i′ = i + 1

Eval(Pre || v▷v : Ty(t) := e Post) ⟶ Eval(Pre v▷v : Ty(t) := e || Post)

Eval(Pre || v▷v : Eq(e′ : t) := e Post) ⟶ Eval(Pre v▷v : Eq(e′ : t) := e || Post)

Eval(Pre || \overline{v}▷\overline{v} : Ty(BT) := P Post) ⟶ Eval(Pre \overline{v}▷\overline{v} : Ty(BT) := P || Post)

Eval(Pre || v▷v : Eq(\overline{p} : BT) := P Post) ⟶

Eval(Pre v▷v : Eq(\overline{p} : BT) := P || Post)

Eval(Pre || v▷v : Ty(M) := p Post) ⟶ Eval(Pre v▷v : Ty(M) := p || Post)

Eval(Pre || v▷v : Eq(p : M) := m Post) ⟶

Eval(Pre v▷v : Eq(p : M) := m || Post)

Eval(Pre || v▷v : Ty(M) := Functor[v̂ : M′]m Post) ⟶ Eval(Pre v▷v : Ty(M) := Functor[v̂ : M′]m || Post)

structure start: q · i ⟶ q · i · 2 · 1

Eval(Pre || v▷v : Ty(M) := Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} EndPost) ⟶ Eval(Pre v▷v : Ty(M) := \underline{Struct} || \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} \underline{End}Post)

structure end: q · i · 2 · n′ ⟶ q · i′ where n′ = n + 1 and i′ = i + 1

Eval(Pre v▷v : Ty(M) := \underline{Struct} \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} || \underline{End}Post) ⟶

Eval(Pre v▷v : Ty(Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End) :=
Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End || Post)

computation: q does not change

Eval(Pre || v▷v : Ty(M) := p_{1} p_{2} Post) ⟶
Eval(Pre || v▷v : Ty(M) := m{v̂ ↦ p_{2}} Post)
if val_{p_{1}}(Pre) = Functor[v̂ : M′]m
Eval(Pre || v▷v : Ty(M) := p_{1} p_{2} Post) ⟶
Eval(Pre || v▷v : Ty(M) := p′ p_{2} Post)
if val_{p_{1}}(Pre) = p′

Lemma 6.1.2. If Pre_{1} is evaluated and Eval(Pre_{1} || Post_{1}) ⟶ Eval(Pre_{2} || Post_{2}) then Pre_{2} is evaluated.

Proof. Obvious.

6.2 Type preservation

In this section we show that every evaluation step preserves the module type of the structure. Given an evaluation step transforming a structure m_{1} into m_{2}, we proceed in two phases. First, we show that specifications in m_{2} are correct and subsume the correspondig specifications in m_{1}. Second, we prove that implementations in m_{2} satisfy thier respective specifications.

Both lemmas are proved by inspecting all evaluation positions q′ in m_{1} or m_{2}. The reasonning depends on the relation between the position q′ and the position of the mark in the evaluation step q (or q · i). Different regions are depicted in Figure 6.1.

II  III  IV  V VI Iqq · i

Figure 6.1: Regions relative to the positions q and q · i

Lemma 6.2.1. Let m_{1} = Struct Pre_{1} Post_{1} End and m_{2} = Struct Pre_{2} Post_{2} End and let Pre_{1} be evaluated. If ⊢ m_{1} : M̂ and Eval(Pre_{1} || Post_{1}) ⟶ Eval(Pre_{2} || Post_{2}) then for every evaluation position q′ in m_{1}, if BV (m_{1}, q′) = \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} then q′ is an evaluation position in m_{2} and BV (m_{2}, q′) subsumes BV (m_{1}, q′) i.e. BV (m_{2}, q′) ⊢ \overline{v}_{i} : S_{i} for all i = 1 … n.

Proof. This is obvious for all simple steps and for structure start, as m_{1} = m_{2}.

A computation step may increase the set of evaluation positions (in case functor application evaluates to a structure), but every evaluation position of m_{1} is an evaluation position of m_{2}. Moreover since specifications do not change, corresponding bound environments are identical.

Let us now consider a structure end step, moving || from q · i · 2 · n′ to q · i′ for i′ = i + 1. The easy cases cover q′ of one of the following forms:

• q′ is lexicographically smaller or equal q · i (regions I, II, III, q, q · i from Figure 6.1),

• q′ is of the form q · i · q′′ for some q′′ (region IV),

• q′ is lexicographically greater than q and q is not a prefix of q′ (region VI).

In all of the above cases, the appropriate environments BV (m_{1}, q′) and BV (m_{2}, q′) are identical.

The remaining positions are those of the form q · j · q′′ with j > i (region V). In that case BV (m_{1}, q′) = E; v : Ty(M); E′ and

BV (m_{2}, q′) = E; v : Ty(Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End); E′

where E = BV (m_{1}, q · i) = BV (m_{2}, q · i).

Since m_{1} is well-typed and

m_{1}|_{q·i} = v▷v : Ty(M) := Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End

then

E ⊢ Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End : M

and hence by inversion

E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End : modtype

and

E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End <: M

Consequently

E; v : Ty(Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End) ⊢ ok

and also

E; v : Ty(Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End) ⊢ v : Ty(M)

Using Lemma 4.1.7, we obtain E; v : Ty(Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End); E′ ⊢ ok and the subsumption obviously holds.

Lemma 6.2.2. Let m_{1} = Struct Pre_{1} Post_{1} End and m_{2} = Struct Pre_{2} Post_{2} End. If ⊢ m_{1} : M̂ and and Eval(Pre_{1} || Post_{1}) ⟶ Eval(Pre_{2} || Post_{2}) then for every evaluation position q′ in m_{2}, such that m_{2}|_{q′} = \overline{v}▷\overline{v} : S := P, we have BV (m_{2}, q′) ⊢ P : S.

Proof. This is obvious for all simple steps and for a structure start step, as m_{1} = m_{2}.

For a computation step performed at position q, if q′ verifies one of the following conditions:

• q′ is lexicographically smaller that q but is not a prefix of q (region II in Figure 6.1),

• q′ is lexicographically greater than q but q is not a prefix of q′ (region VI),

the subterms m_{2}|_{q′} and m_{1}|_{q′} are identical and moreover BV (m_{1}, q′) = BV (m_{2}, q′) so BV (m_{2}, q′) ⊢ P : S follows immediately from the corresponding fact resulting from the correctness of m_{1}.

Now let us consider evaluation positions q′ which have prefix q (region III-V). First, let q′ = q. Let us remind, that m_{1}|_{q} = v▷v : Ty(M) := p_{1} p_{2} and m_{2}|_{q} equals either v▷v : Ty(M) := p′ p_{2} if val_{p_{1}}(Pre) = p′ or v▷v : Ty(M) := m{v̂ ↦ p_{2}} if val_{p_{1}}(Pre) = Functor[v̂ : M′]m.

Let us denote the environment BV (m_{1}, q) = BV (m_{2}, q) by E. From the correctness of m_{1}, it follows that E ⊢ p_{1} p_{2} : M. Using inversion and principal typing properties, we obtain E ⊢_{P} p_{1} : Funsig(v̂ : M_{1})M_{2} and E ⊢ p_{2} : M_{1} and E ⊢ M_{2}{v̂ ↦ p_{2}} <: M. By Lemma 6.1.1, E ⊢ val_{p_{1}}(Pre) : Funsig(v̂ : M_{1})M_{2}.

If val_{p_{1}}(Pre) = p′, then obviously E ⊢ p′ p_{2} : M_{2}{v̂ ↦ p_{2}} and E ⊢ p′ p_{2} : M by subsumption and E ⊢ p′ p_{2} : Ty(M) by specification satisfaction.

If val_{p_{1}}(Pre) = Functor[v̂ : M′]m then by inversion E ⊢ M_{1} <: M′ and E; v̂ : Ty(M′) ⊢ m : M_{2}. Since E ⊢ p_{2} : M_{1}, we have E ⊢ p_{2} : M′ and by Lemma 4.1.5, E ⊢ m{v̂ ↦ p_{2}} : M_{2}{v̂ ↦ p_{2}}. By subsumption and specification satisfaction, E ⊢ m{v̂ ↦ p_{2}} : Ty(M).

If q ≠ q′ and p_{1} p_{2} evaluates to a structure, q′ may be of the form q · 2 · q′′ for some q′′. The correctness of the corresponding m_{2}|_{q′} = m{v̂ ↦ p_{2}}|_{q′′} follows from the correctness of m{v̂ ↦ p_{2}}.

To end up the computation step we are left with the positions q′ which are prefixes of q (region I). We delay the consideration of this case until we study the structure end step.

Consider now a structure end step moving the mark || from q · i · 2 · n′ to q · i′ where i′ = i + 1. If q′ satisfies one of the following conditions:

• q′ is lexicographically smaller than q · i but is not a prefix of q · i (regions II, III)

• q′ is of the form q · i · q′′ for some q′′ ≠ ϵ (region IV),

• q′ is lexicographically greater than q but q is not a prefix of q′ (region VI),

the corresponding subterms and bound environments are identical.

Now let us consider q′ = q · i. Let us denote BV (m_{1}, q′) = BV (m_{2}, q′) by E. Since m_{1} is well-typed and

m_{1}|_{q·i} = v▷v : Ty(M) := Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End

then

E ⊢ Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End : M

and hence by inversion

E ⊢ Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End : Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End

So since

m_{2}|_{q·i} = v▷v : Ty(Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End)
:= Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End

this structure component is correct.

For all evaluation positions q′ of the form q′ = q · j · q′′ with j > i (region V), we have m_{1}|_{q′} = m_{2}|_{q′}, and from the previous lemma (6.2.1) BV (m_{2}, q′) subsumes BV (m_{1}, q′).

Now we are left with positions q′ which are prefixes of q (region I and q for the case of a computation step) or q · i (region I, q and q · i for a structure end step).

We will show the correctness of the corresponding subterms by induction on decreasing length of q′. If q′ = q or q′ = q · i respectively, it has already been proven.

For shorter q′, we have

m_{1}|_{q′} = v▷v : Ty(M)
:= Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … v_{j}▷v_{j} : Ty(M′_{1}) := m′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End

and

m_{2}|_{q′} = v▷v : Ty(M)
:= Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … v_{j}▷v_{j} : Ty(M′_{2}) := m′_{2} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End

Note that M′_{1} and M′_{2} are not identical only for such q′ that q′ · 2 = q and j = i and we are dealing with a structure end step which moved the mark to from q · i to q · i′ for i′ = i + 1.

Since m_{1} is correct,

BV (m_{1}, q′) ⊢ Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … v_{j}▷v_{j} : Ty(M′_{1}) := m′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End : M

and by inversion

BV (m_{1}, q′) ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … v_{j}▷v_{j} : Ty(M′_{1}) … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End <: M

From Lemma 6.2.1 BV (m_{2}, q′ · 2 · n′) = BV (m_{2}, q′); \overline{v}_{1} : S_{1} … v_{i} : Ty(M′_{2}) … \overline{v}_{n} : S_{n} subsumes BV (m_{1}, q′·2·n′) = BV (m_{1}, q′); \overline{v}_{1} : S_{1} … v_{i} : Ty(M′_{1}) … \overline{v}_{n} : S_{n} where n′ = n+1.

Since BV (m_{2}, q′) = BV (m_{1}, q′),

BV (m_{2}, q′) ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … v_{i}▷v_{i} : Ty(M′_{2}) … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End <: Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … v_{i}▷v_{i} : Ty(M′_{1}) … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End

and consequently, using transitivity of subtyping, if

BV (m_{1}, q′) ⊢ Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … v_{i}▷v_{i} : Ty(M′_{1}) := m′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End : Ty(M)

then also

BV (m_{2}, q′) ⊢ Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … v_{i}▷v_{i} : Ty(M′_{2}) := m′_{2} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End : Ty(M)

Lemma 6.2.3. Let m_{1} = Struct Pre_{1} Post_{1} End and m_{2} = Struct Pre_{2} Post_{2} End. If ⊢ m_{1} : M̂ and and Eval(Pre_{1} || Post_{1}) ⟶ Eval(Pre_{2} || Post_{2}) then ⊢ m_{2} : M̂.

Proof. If

m_{1} = Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End

and

m_{2} = Struct \overline{v}_{1}▷\overline{v}_{1} : S′_{1} := P′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S′_{n} := P′_{n} End

using Lemmas 6.2.2 and 6.2.1 and the reasoning similar to the last part of the proof of the previous lemma, we can show that ⊢ m_{2} : Sig \overline{v}_{1}▷\overline{v}_{1} : S′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S′_{n} End and ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S′_{n} End <: Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End. Hence, if ⊢ m_{1} : M̂ then also ⊢ m_{2} : M̂.

6.3 Termination

In this section we prove that evaluation is terminating. Lemma 3.2.1 announced in Chapter 3 is a consequence of this fact and of the previous section.

In Section 6.3.1 we define a translation of expressions of our module system into the Weak Module Calculus of Courant [32], a calculus in which term typing is trivial and in which module reductions are terminating. Section 6.3.2 shows that the translation preserves types: the translation of a correct expression of our calculus is correct in WMC.

Next, in Section 6.3.3 we show that computation steps correspond to module reductions on translated expressions and therefore every sequence of computation steps is finite. Unfortunately structure end evaluation steps imply modifications of the translated expressions which do not correspond to module reductions.

But on the other hand every evaluation step, apart from computation, moves the mark to the right, to the next evaluation position. Hence the rest of the termination proof aims at showing that the set of all possible evaluation positions is bounded. The bound is equal to the number of evaluation positions in the expression obtained by reducing in WMC the translation of the first expression in the evaluation sequence until the normal form.^{1}

The only difficulty remains in showing that the bound is not increased by the modifications performed by the structure end step. To do this, in Sections 6.3.4 and 6.3.5 we define a certain reduction strategy in WMC and prove that if the reduction follows this strategy until normal forms, it is easy to see that the structure end step does not increase the bound.

Hence we obtain the termination of the whole evaluation process (Section 6.3.6).

6.3.1 Translation into WMC

Below we define a translation of expressions of our syntax into the Weak Module Calculus of Courant [32]. The latter calculus is used in the work of Courant as an intermediate calculus whose role is to separate problems related with term typing from module typing. It is weak in a sense that typing of terms is trivial, i.e. for all terms e and t we have e : t. As shown in [32], module reductions (i.e. β_{m}δ-reductions) of expressions well-typed in WMC are terminating (Theorem 5.4.10).

The definition presented below translates most classes of expressions from our calculus into WMC. Since we are only interested in module reductions, we get rid of terms and blocks during the translation. We replace term and block components in environments, signatures and structures by (sequences) of trivial term components v : Eq(⋆ : ⋆) (which are correct in WMC). Apart from this, environments and module types are translated as is, and the translation of structures (and consequently structure components) depends on the position of the mark.

Strictly formally what we define for module expressions and structure components are three functions: when the mark is before the translated element, after that element, and inside that element. In the latter case the output of the function depends on the exact position of the mark. To simplify the presentation, we write W ( || m) and W (m || ) for first two cases and something like W (Struct Pre || Post End) in the latter case. In the formulation of lemmas we often write “… W (m) … wherever the mark is in m” to mean that the result holds for all three translating functions and for all positions of the mark in case of the third function.

1In fact, for the sake of simplicity, we use a bit greater bound.

Definition 6.3.1. The translation W (·) is defined as follows:

• structure component sequence StrCs

W (\overline{v}▷\overline{v} : S := P; Pre || Post) = W (\overline{v}▷\overline{v} : S := P || ); W (Pre || Post)

W (v▷v : S := Struct Pre || Post End; StrCs) = W (v▷v : S := Struct Pre || Post End); W ( || StrCs)

W ( || \overline{v}▷\overline{v} : S := P; StrCs) = W ( || \overline{v}▷\overline{v} : S := P); W ( || StrCs)

• structure component

– abstract module

W (v▷v : Ty(M) := m || ) = v▷v : Eq(m′ : M′) := m′ where m′ = W (m || ) and M′ = SIG(m′) if m is a structure and M′ = W (M) otherwise

W (v▷v : Ty(M) := Struct Pre || Post End) = v▷v : Eq(m′ : W (M)) := m′ where m′ = W (Struct Pre || Post End).

W ( || v▷v : Ty(M) := m) = v▷v : Eq(m′ : W (M)) := m′ where m′ = W ( || m)

– manifest module (after or before the mark)

W (v▷v : Eq(p : M) := p′) =

v▷v : Eq(p : W (M)) := p′

– block or term (after or before the mark)

W (\overline{v}▷\overline{v} : S := P) = v^{1}▷v^{1} : Eq(⋆ : ⋆) := ⋆ … v^{n}▷v^{n} : Eq(⋆ : ⋆) := ⋆ where \overline{v} = v^{1} … v^{n}

• module expression

W (Struct StrCs End || ) =

Struct W (StrCs || ) End

W (Struct Pre || Post End) =

Struct W (Pre || Post) End

W ( || Struct StrCs End) =

Struct W ( || StrCs) End

W (Functor[v : M]m || ) = W ( || Functor[v : M]m) =

Functor[v : W (M)]W ( || m) W (p || ) = W ( || p) =

p W ( || p_{1} p_{2}) = W (p_{1} p_{2} || ) =

p_{1} p_{2}

• module type

W (Sig SigCs End) =

Sig W (SigCs) End

W (Funsig(v : M)M′) =

Funsig(v : W (M))W (M′)

• signature component sequence SigCs

W (v_{1} : S_{1} … v_{n} : S_{n}) =

W (v_{1} : S_{1}) … W (v_{n} : S_{n})

• signature component W (v▷v : Ty(M))) = v▷v : Ty(W (M)) W (v▷v : Eq(p : M)) = v▷v : Eq(p : W (M)) W (\overline{v}▷\overline{v} : S := P) = v^{1}▷v^{1} : Eq(⋆ : ⋆); …; v^{n}▷v^{n} : Eq(⋆ : ⋆) where \overline{v} = v^{1} … v^{n}

• specification

W (Ty(M)) =

Ty(W (M)) W (Eq(p : M)) =

Eq(p : W (M)) W (S) =

Eq(⋆ : ⋆) … Eq(⋆ : ⋆) where the lengths of S and of the resulting sequence are equal, if S is not a module specification.

It is easy to see that given a marked module expression m and an evaluation position q in m, we have:

• W (m)|_{q} = W ( || m|_{q}) if the mark is before the position q,

• W (m)|_{q} = W (m|_{q}) if q is a prefix of the position of the mark,

• W (m)|_{q} = W (m|_{q} || ) otherwise

6.3.2 Correctness of the translation

We will now prove that the translation of expressions well-typed in ⊢ yields expressions which are well-typed in ⊢_{W}. We start by proving some simple properties, then by induction on the derivation we show correctness of the translation of module types and specifications and we conclude by showing that translating structures is correct.

Lemma 6.3.1. For all specifications S and module types M, W (S){v ↦ p} = W (S{v ↦ p}) and W (M){v ↦ p} = W (M{v ↦ p}). Moreover for any module expressions m W (m){v ↦ p} = W (m{v ↦ p}) wherever the mark is in m.

Proof. By mutual induction on the structure of S and M respectively and by induction on the structure of m.

Note that the definition of strengthening in [32] differs slightly from ours. Indeed, in our calculus

Funsig(v : M)M′/p = Funsig(v : M)M′

while in [32], we have:

Funsig(v : M)M′/p = Funsig(v : M)(M′/(p v))

Lemma 6.3.2. Let E be an environment. For every path p, module type M and specification S,

• if E ⊢ p : M and W (E) ⊢_{W} p : W (M) then W (E) ⊢_{W} W (M/p) : modtype and W (E) ⊢_{W} W (M)/p <: W (M/p),

• if E ⊢ p : S and W (E) ⊢_{W} p : W (S) then W (E) ⊢_{W} W (S/p) : spec and W (E); v : W (S)/p ⊢_{W} v : W (S/p).

Proof. By induction on the structure of M and S respectively, similarly to Lemmas 5.2.8 and 5.2.9 from [32]

Lemma 6.3.3. The translation of path, module type, specification and environment judgments is correct, i.e.

• if E ⊢ p : M then W (E) ⊢_{W} W (p) : W (M),

• if E ⊢ p : S for a module specification S then W (E) ⊢_{W} W (p) : W (S),

• if E ⊢ p_{1} ≈ p_{2} for module paths p_{1} and p_{2} then W (E) ⊢_{W} W (p_{1}) ≈ W (p_{2}),

• if E ⊢ M : modtype then W (E) ⊢_{W} W (M) : modtype,

• if E ⊢ M_{1} <: M_{2} then W (E) ⊢_{W} W (M_{1}) <: W (M_{2}),

• if E ⊢ ok then W (E) ⊢_{W} ok,

• if E ⊢ S : spec then W (E) ⊢_{W} S^{i} : spec for each S^{i} in W (S).

Proof. Induction on the derivation of E ⊢ J:

For (Env/Empty) it is obvious.

For (Env/Insert) we get W (E; \overline{v} : S) = W (E); W (\overline{v} : S), and W (E; \overline{v} : S) ⊢_{W} ok by induction hypothesis and application of the rule [/ENV/VAR].

For (Env/Lookup), we get W (E_{1}; \overline{v} : S; E_{2}) ⊢_{W} ok by induction hypothesis and if \overline{v} : S is a module judgment v : S, then W (E_{1}; v : S; E_{2}) = W (E_{1}); v : W (S); W (E_{2}) and W (E_{1}; v : S; E_{2}) ⊢_{W} v : W (S) by [/S/VAR].

For (Sig/Form), by induction hypothesis: W (E); W (\overline{v}_{1} : S_{1}); …; W (\overline{v}_{n} : S_{n}) ⊢_{W} ok and hence W (E) ⊢_{W} SigW (\overline{v}_{1}▷\overline{v}_{1} : S_{1}); …; W (\overline{v}_{n}▷\overline{v}_{n} : S_{n}) End : modtype by [M/MT/EMPTY] and successive applications of [M/MT/DECL].

For (Sig/Access), since W (p.v_{i}) = p.v_{i}, by induction hypothesis we have

W (E) ⊢_{W} p : W (Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End)

Since

W (Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End) = Sig W (\overline{v}_{1}▷\overline{v}_{1} : S_{1}) … W (\overline{v}_{n}▷\overline{v}_{n} : S_{n}) End

and since p.v_{i} is a module path, then W (v_{i}▷v_{i} : S_{i}) = v_{i}▷v_{i} : W (S_{i}) and by [/S/ACCESS] in WMC, we get W (E) ⊢_{W} p.v_{i} : W (S_{i})\{\overline{v}_{j} ↦ \overline{p.v_{j}}\}. Since the translation commutes with path substitutions, we get the desired conclusion W (E) ⊢_{W} p.v_{i} : W (S_{i}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\}).

For (Sig/Sub), we get

W (E) ⊢_{W} Sig W (\overline{v}_{1}▷\overline{v}_{1} : S_{1}) … W (\overline{v}_{n}▷\overline{v}_{n} : S_{n}) End : modtype

and

W (E) ⊢_{W} Sig W (\overline{v}′_{1}▷\overline{v}′_{1} : S′_{1}) … W (\overline{v}′_{n′}▷\overline{v}′_{n′} : S′_{n′}) End : modtype

from induction hypothesis. For term components, we have

W (E); W (\overline{v}_{1} : S_{1}) … W (\overline{v}_{n} : S_{n}) ⊢_{W} v′_{i}^{j} : Eq(⋆ : ⋆)

because that is the translation of the specification of v′_{i} in the environment. For module components, we have

W (E); W (\overline{v}_{1} : S_{1}) … W (\overline{v}_{n} : S_{n}) ⊢_{W} v′_{i} : W (S′_{i})

by induction hypothesis about paths. So, applying [M/SUB/SIG] in WMC, we get

W (E) ⊢_{W} Sig W (\overline{v}_{1}▷\overline{v}_{1} : S_{1}) … W (\overline{v}_{n}▷\overline{v}_{n} : S_{n}) End <: Sig W (\overline{v}′_{1}▷\overline{v}′_{1} : S′_{1}) … W (\overline{v}′_{n′}▷\overline{v}′_{n′} : S′_{n′}) End

which is the desired conclusion.

The rules (Funsig/Form) and (Funsig/Sub) are obvious by induction hypothesis and corresponding rules [M/MT/PROD] and [M/SUB/PROD] of WMC.

The (Subsumption) rule (where m is a path) follows from the induction hypothesis and [M/T/SUB].

The (Strengthening) rule follows from the induction hypothesis, Lemma 6.3.2 and [M/T/STR].

For (Ty/Form), if Ty(Ψ) is a module specification, then W (Ty(Ψ)) is a sequence of Eq(⋆ : ⋆), which are correct specifications in WMC, by the rules [T/T/AXIOM], [T/T/TERM] and [/WS/MAN]. If Ty(Ψ) is a module specification, then Ψ is a module type M and Ξ is modtype. By induction hypothesis W (E) ⊢_{W} W (M) : modtype and hence W (E) ⊢_{W} Ty(W (M)) : spec.

Rules (Ty/Elim) and (Ty/Sat) for module specifications follow from the induction hypothesis and rules [/T/ABS] and [/S/ABS].

For (Eq/Form), again if Eq(ϕ : Ψ) is not a module specification, than the translation yields a sequence of Eq(⋆ : ⋆), which are all correct in WMC.

If Eq(ϕ : Ψ) is a module specification Eq(p : M), then W (E) ⊢_{W} p : W (M) by induction hypothesis and W (E) ⊢_{W} W (M) : modtype. Hence W (E) ⊢_{W} Eq(p : W (M)) : spec by [/WS/MAN] which is the desired conclusion.

The rules (Eq/Sat), (Eq/Type) and (Eq/Comp) for module specifications follow easily by induction hypothesis and rules [/S/MAN], [/T/MAN] and [/E/SPEC].

Equality rules on module paths: (Conv/Refl), (Conv/Trans), (Conv/Symm) and (Conv/Path) follow from induction hypothesis and rules [/E/MU], [/E/TRANS], [/E/SYM] and [/E/ACCESS], respectively.

We say that a module expression m is a partially evaluated marked module expression if m is evaluated up to the mark. In other words, if m = Struct Pre || Post End then Pre is evaluated and if the mark is after m then the whole module expression m is evaluated and in particular it cannot be a path application.

Lemma 6.3.4. Let m be a partially evaluated marked module expression. If E ⊢ m : M then W (E) ⊢_{W} W (m) : W (M), wherever the mark is in m. Moreover if m is a structure Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End then

W (m) = Struct

v_{1}^{1}▷v_{1}^{1} : S_{1}^{1}′ := P_{1}^{1}′ … v_{1}^{k_{1}}▷v_{1}^{k_{1}} : S_{1}^{k_{1}}′ := P_{1}^{k_{1}}′ …

v_{n}^{1}▷v_{n}^{1} : S_{n}^{1}′ := P_{n}^{1}′ … v_{n}^{k_{n}}▷v_{n}^{k_{n}} : S_{n}^{k_{n}}′ := P_{n}^{k_{n}}′ End

where v_{1}^{1} … v_{1}^{k_{1}} = \overline{v}_{1}, …, v_{n}^{1} … v_{n}^{k_{n}} = \overline{v}_{n} and

W (E) ⊢_{W} SIG(W (m)) <: W (SIG(m))

Proof. By induction on the structure of m, using the previous lemma.

• If m is a structure, then by inversion E ⊢ SIG(m) <: M and by Lemma 6.3.3, W (E) ⊢_{W} W (SIG(m)) <: W (M).

Now we show by induction on i that for every i = 1 … n and every j = 1 … k_{i}

E_{i−1} ⊢_{W} P_{i}^{j}′ : S_{i}^{j}′ and E_{i} ⊢_{W} v_{i}^{j} : Ŝ_{i}^{j}

where E_{l} = W (E); v_{1}^{1} : S_{1}^{1}′ … v_{1}^{k_{1}} : S_{1}^{k_{1}}′ … v_{l}^{1} : S_{l}^{1}′ … v_{l}^{k_{l}} : S_{l}^{k_{l}}′ and Ŝ_{i}^{1} … Ŝ_{i}^{k_{i}} = W (S_{i})

– If v_{i}▷v_{i} : S_{i} := P_{i} is not a module component, then the resulting fragment of the structure W (m) is

v_{i}^{1}▷v_{i}^{1} : Eq(⋆ : ⋆) := ⋆; …; v_{i}^{k_{i}}▷v_{i}^{k_{i}} : Eq(⋆ : ⋆) := ⋆

and Ŝ_{i}^{j} = Eq(⋆ : ⋆) for all j. Hence the second judgment holds trivially and the first follows from the fact that in any environment Ê, we have Ê ⊢_{W} ⋆ : Eq(⋆ : ⋆)

– If v_{i}▷v_{i} : S_{i} := P_{i} is a module judgment, then k_{i} = 1. If P_{i} is a structure m′, then we consider two cases, depending on the position of the mark in m. If the mark is after i, then according to the definition of the translation function, S_{i} = Ty(SIG(m′)), P_{i}^{1}′ = W (m′ || ) and S_{i}^{1}′ = Eq(W (m′ || ) : SIG(W (m′ || ))). Since

E; \overline{v}_{1} : S_{1}; …; \overline{v}_{i−1} : S_{i−1} ⊢ m′ : SIG(m′)

by big induction hypothesis we have

W (E); W (\overline{v}_{1} : S_{1}); …; W (\overline{v}_{i−1} : S_{i−1}) ⊢_{W} W (m′ || ) : W (SIG(m′))

and

W (E); W (\overline{v}_{1} : S_{1}); …; W (\overline{v}_{i−1} : S_{i−1}) ⊢_{W} SIG(W (m′ || )) <: W (SIG(m′))

Since W (m′ || ) is typable, it can also be typed by its primary signature SIG(W (m′ || )). Using the second part of the small induction hypothesis for smaller i we can show that E_{i−1} subsumes W (E); W (\overline{v}_{1} : S_{1}); …; W (\overline{v}_{i−1} : S_{i−1}). By weakening lemma for WMC we get:

E_{i−1} ⊢_{W} W (m′ || ) : SIG(W (m′ || ))

and

E_{i−1} ⊢_{W} SIG(W (m′ || )) <: W (SIG(m′))

From the latter two judgments, using subsumption and specification rules we get

E_{i−1} ⊢_{W} W (m′ || ) : Eq(W (m′ || ) : SIG(W (m′ || )))

and

E_{i−1}; v_{i} : Eq(W (m′ || ) : SIG(W (m′ || ))) ⊢_{W} v_{i} : Ty(W (SIG(m′)))

which are the two facts that we have to prove, since W (S_{i}) = Ty(W (SIG(m′))).

If the mark in m is inside or after i, then S_{i} = Ty(M′) for some module type M′ and S_{i}^{1}′ = Eq(W (m′) : Ty(W (M′)). By the big induction hypothesis, we get

W (E); W (\overline{v}_{1} : S_{1}); …; W (\overline{v}_{i−1} : S_{i−1}) ⊢_{W} W (m′) : W (M′)

and by weakening and specification satisfaction

E_{i−1} ⊢_{W} W (m′) : Eq(W (m) : W (M′))

which is the first inductive fact that we have to prove, as P_{i}^{1} = W (m′). The other is very simple since W (S_{i}) = Ty(W (M′)) and obviously

E_{i−1}; v_{i} : Eq(W (m′) : W (M′)) ⊢_{W} v_{i} : Ty(W (M′))

– If v▷v : S_{i} := P_{i} is a module component where S_{i} is an abstract specification and P_{i} is not a structure, then we proceed exactly like in the second case for structures.

– If v▷v : S_{i} := P_{i} is a module component where S_{i} is a manifest specification, then S_{i} = Eq(p : M′), P_{i} = p′ and S_{i}^{1}′ = Eq(p : W (M′)) and P_{i}^{1}′ = p′. Since W (S_{i}) = S_{i}^{1}′ = Eq(p : W (M′)) we can simply use Lemma 6.3.3 to get

W (E); W (\overline{v}_{1} : S_{1}); …; W (\overline{v}_{i−1} : S_{i−1}) ⊢_{W} p′ : Eq(p : W (M′))

and weakening to get E_{i−1} ⊢_{W} p′ : Eq(p : W (M′)). The second inductive fact is trivial.

Let us summarize the case where m is a structure. From first inductive facts we get the correctness of all implementations of the structure W (m), which gives us

W (E) ⊢_{W} W (m) : SIG(W (m))

From the second inductive facts, we have

W (E) ⊢_{W} SIG(W (m)) <: Sig W (\overline{v}_{1}▷\overline{v}_{1} : S_{1}) … W (\overline{v}_{n}▷\overline{v}_{n} : S_{n}) End

which is W (E) ⊢_{W} SIG(W (m)) <: W (SIG(m)).

Using subsumption and the fact that W (E) ⊢_{W} W (SIG(m)) <: W (M) following from inversion and Lemma 6.3.3, we get the desired conclusion W (E) ⊢_{W} W (m) : W (M).

• If m is of the form Functor(v̂ : M′)m′ then regardless of the position of the mark, its translation is always Functor(v̂ : W (M′))W ( || m′). So if E ⊢ Functor[v̂ : M′]m′ : M, then by inversion we have E ⊢ Functor[v̂ : M′]m′ : Funsig(v̂ : M′)M′′ and E ⊢ Funsig(v̂ : M′)M′′ <: M. Again, by inversion E; v̂ : Ty(M′) ⊢ m′ : M′′. Using Lemma 6.3.3, we have

W (E) ⊢_{W} W (Funsig(v̂ : M′)M′′) <: W (M)

By induction hypothesis, W (E); v̂ : W (M′) ⊢_{W} W ( || m) : W (M′′) and hence W (E) ⊢_{W} Functor[v̂ : W (M′)]W ( || m) : Funsig(v̂ : W (M′))W (M′′), which is equivalent to W (E) ⊢_{W} W (Functor[v̂ : M′]m) : W (Funsig(v̂ : M′)M′′). By subsumption, we obtain W (E) ⊢_{W} W (Functor[v̂ : M′]m) : W (M).

• If m is a path p then we can immediately apply Lemma 6.3.3.

• If m is an application of paths p_{1} p_{2}, then by inversion we get the following judgments: E ⊢ p_{1} : Funsig(v̂ : M′)M′′, E ⊢ p_{2} : M′ and E ⊢ M′′{v̂ ↦ p_{2}} <: M. We can apply Lemma 6.3.3 to all of them and get W (E) ⊢_{W} p_{1} : W (Funsig(v̂ : M′)M′′), W (E) ⊢_{W} p_{2} : W (M′) and W (E) ⊢_{P} W (M′′{v̂ ↦ p_{2}}) <: W (M).

Since the following equalities hold

W (Funsig(v̂ : M′)M′′) = Funsig(v̂ : W (M′))W (M′′)
W (M′′{v̂ ↦ p_{2}}) = W (M′′){v̂ ↦ p_{2}}
W (p_{1} p_{2}) = p_{1} p_{2}

we can use the application and subsumption to get the desired conclusion

W (E) ⊢_{W} p_{1} p_{2} : W (M)

Corollary 6.3.5. Let m = Struct Pre || Post End be a partially evaluated marked structure. If ⊢ m : M then ⊢_{W} W (Struct Pre || Post End) : W (M)

6.3.3 Computation is a delta-mu reduction

Given a computation step evaluating a module path application p_{1} p_{2}, the first lemma in this section shows that in the translated term p_{1} δ-reduces to the translation of its value val_{p_{1}}(Pre). The second lemma shows that the computation step corresponds to a non-empty sequence of μδ-reductions between the translated structures.

Lemma 6.3.6. Let ⊢ Struct Pre || Post End : M̂ where q is a position of the mark and Pre is evaluated. If val_{p′}(Pre) = m then

BV (W (Struct Pre || Post End), q)) ⊢_{W} p′ ⟶_{δ} W (m || )

Moreover if m is a structure, then

BV (W (Struct Pre || Post End), q)) ⊢_{δ} p′ : Eq(W (m || ) : SIG(W (m || )))

Proof. By induction on the structure of the path p′.

• If p′ is a variable v, then either Pre = Pre_{1} v▷v : Eq(p : M) := m Pre_{2} and val_{v}(Pre) = p or Pre = Pre_{1} v▷v : Ty(M) := m Pre_{2} and val_{v}(Pre) = m. In the first case m = p̂ for some p̂ and we have

BV (W (Struct Pre_{1} v▷v : Eq(p : M) := m Pre_{2} || Post End, q) = E_{1}; v : Eq(p : M′); E_{2}

for some E_{1}, E_{2} and M′ and hence

BV (W (Struct Pre || Post End, q) ⊢_{W} v ⟶_{δ} p

In the second case

BV (W (Struct Pre_{1} v▷v : Ty(M) := m Pre_{2} || Post End, q) = E_{1}; v : Eq(W (m || ) : M′); E_{2}

for some E_{1}, E_{2} and M′. Consequently,

E_{1}; v : Eq(W (m || ) : M′); E_{2} ⊢_{δ} v : Eq(W (m || ) : M′)

If m is a structure then by definition of W (v▷v : Ty(M) := m || ), M′ = SIG(W (m || )).

• If p′ = p.v_{i} and val_{p}(Pre) = p′′ then val_{p.v_{i}}(Pre) = p′′.v_{i}

By induction hypothesis: BV (W (Struct Pre || Post End), q)) ⊢_{W} p ⟶_{δ} p′′ because p′′ = W (p′′ || ). Hence BV (W (Struct Pre || Post End), q)) ⊢_{W} p.v_{i} ⟶_{δ} p′′.v_{i}.

• If p′ = p.v_{i} and val_{p}(Pre) = m where

m = Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … v_{i}▷v_{i} : S_{i} := m_{i} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End

then either S_{i} = Eq(p′′ : M) and val_{p.v_{i}}(Pre) = p′′δ, where δ = \{\overline{v}_{j} ↦ \overline{p.v_{j}}\} or S_{i} = Ty(M) and val_{p.v_{i}}(Pre) = m_{i}δ.

By definition of W (·),

W (m || ) = Struct

v_{1}^{1}▷v_{1}^{1} : S_{1}^{1}′ := P_{1}^{1}′ … v_{1}^{k_{1}}▷v_{1}^{k_{1}} : S_{1}^{k_{1}}′ := P_{1}^{k_{1}}′ … v_{i}▷v_{i} : S′_{i} := m′_{i} …

v_{n}^{1}▷v_{n}^{1} : S_{n}^{1}′ := P_{n}^{1}′ … v_{n}^{k_{n}}▷v_{n}^{k_{n}} : S_{n}^{k_{n}}′ := P_{n}^{k_{n}}′ End By induction hypothesis

BV (W (Struct Pre || Post End), q)) ⊢_{δ} p : Eq(W (m || ) : SIG(W (m || )))

and hence

BV (W (Struct Pre || Post End), q)) ⊢_{δ} p.v_{i} : S′_{i}δ

If S_{i} = Eq(p′′ : M) then S′_{i} = Eq(p′′ : W (M)) and hence

BV (W (Struct Pre || Post End), q)) ⊢_{δ} p.v_{i} : Eq(p′′δ : W (M)δ)
BV (W (Struct Pre || Post End), q)) ⊢_{W} p.v_{i} ⟶_{δ} p′′δ

Otherwise S′_{i} = Eq(m′_{i} : M′), with m′_{i} = W (m_{i} || ) and M′ = SIG(m′_{i}) if m_{i} is a structure. Consequently

BV (W (Struct Pre || Post End), q)) ⊢_{δ} p.v_{i} : Eq(m′_{i}δ : M′δ)
BV (W (Struct Pre || Post End), q)) ⊢_{W} p.v_{i} ⟶_{δ} m′_{i}δ

By Lemma 6.3.1 W (m_{i}δ || ) = W (m_{i} || )δ, so the first part of the lemma is proved.

If m_{i} is a structure, so are m′_{i} and m′_{i}δ. Hence, since M′ = SIG(m′_{i}) then also M′δ = SIG(m′_{i})δ = SIG(m′_{i}δ), which concludes the proof of the second part.

Lemma 6.3.7. Let ⊢ Struct Pre_{1} Post_{1} End : M with Pre_{1} evaluated. If

Eval(Pre_{1} || Post_{1}) ⟶ Eval(Pre_{2} || Post_{2})

by a computation step, then

⊢_{W} W (Struct Pre_{1} || Post_{1} End) ⟶_{μδ}^{+} W (Struct Pre_{2} || Post_{2} End)

Proof. Let m̂_{1} = Struct Pre_{1} || Post_{1} End and m̂_{2} = Struct Pre_{2} || Post_{2} End and let m_{1} = W (m̂_{1}) and m_{2} = W (m̂_{2}). Let q′ be the position in m̂_{1} at which the computation step is performed.

We show by induction on the decreasing length of even prefixes q of q′ · 2 that

BV (m_{1}, q) ⊢_{W} m_{1}|_{q} ⟶_{μδ}^{+} m_{2}|_{q}

Since q′ is a shape position, its length is odd, therefore the longest q is q′ · 2. We have m̂_{1}|_{q′} = v▷v : Ty(M) := p_{1} p_{2} and hence

m̂_{1}|_{q′·2} = m_{1}|_{q′·2} = p_{1} p_{2} and BV (m_{1}, q′ · 2) = BV (m_{1}, q′)

If val_{p_{1}}(Pre_{1}) = p′, then by Lemma 6.3.6, BV (m_{1}, q′) ⊢_{W} p_{1} ⟶_{δ} p′. Hence, we also have BV (m_{1}, q′ · 2) ⊢_{W} p_{1} p_{2} ⟶_{δ} p′ p_{2}, where p′ p_{2} = m_{2}|_{q′·2} = m̂_{2}|_{q′·2}.

If val_{p_{1}}(Pre_{1}) = Functor[v̂ : M′]m, then BV (m_{1}, q′) ⊢_{W} p_{1} ⟶_{δ} W (Functor[v̂ : M′]m || ). We have m̂_{2}|_{q′·2} = m{v̂ ↦ p_{2}} and m_{2}|_{q′·2} = W ( || m{v̂ ↦ p_{2}}). Since

W (Functor[v̂ : M′]m || ) = Functor[v̂ : W (M′)]W ( || m)

we have

BV (m_{1}, q′ · 2) ⊢_{W} p_{1} p_{2} ⟶_{δ} Functor[v̂ : W (M′)]W ( || m) p_{2} ⟶_{μ} W ( || m){v̂ ↦ p_{2}} The latter expression is equal W ( || m{v̂ ↦ p_{2}}) and hence

BV (m_{1}, q′ · 2) ⊢_{W} m_{1}|_{q′·2} ⟶_{μδ}^{+} m_{2}|_{q′·2}

If q is a strict even prefix of q′ · 2 then q′ · 2 = q · j · 2 · q̂ for some j and q̂ of even length. Therefore

m_{1}|_{q} = Struct

v_{1}^{1}▷v_{1}^{1} : S_{1}^{1}′ := P_{1}^{1}′ … v_{1}^{k_{1}}▷v_{1}^{k_{1}} : S_{1}^{k_{1}}′ := P_{1}^{k_{1}}′ … v_{j}▷v_{j} : Eq(m′_{1} : W (M′)) := m′_{1} … v_{n}^{1}▷v_{n}^{1} : S_{n}^{1}′ := P_{n}^{1}′ … v_{n}^{k_{n}}▷v_{n}^{k_{n}} : S_{n}^{k_{n}}′ := P_{n}^{k_{n}}′

End

where m′_{1} = W (m̂_{1}|_{q·j·2}) and

m_{2}|_{q} = Struct

v_{1}^{1}▷v_{1}^{1} : S_{1}^{1}′ := P_{1}^{1}′ … v_{1}^{k_{1}}▷v_{1}^{k_{1}} : S_{1}^{k_{1}}′ := P_{1}^{k_{1}}′ … v_{j}▷v_{j} : Eq(m′_{2} : W (M′)) := m′_{2} … v_{n}^{1}▷v_{n}^{1} : S_{n}^{1}′ := P_{n}^{1}′ … v_{n}^{k_{n}}▷v_{n}^{k_{n}} : S_{n}^{k_{n}}′ := P_{n}^{k_{n}}′

End

where m′_{2} = W (m̂_{2}|_{q·j·2}).

Since m′_{1} = m_{1}|_{q·j·2} and m′_{2} = m_{2}|_{q·j·2}, by induction hypothesis

BV (m_{1}, q · j · 2) ⊢_{W} m′_{1} ⟶_{μδ}^{+} m′_{2}

Since BV (m_{1}, q · j · 2) = BV (m_{1}, q · j · 1 · 1), we also have

BV (m_{1}, q · j · 1 · 1) ⊢_{W} m′_{1} ⟶_{μδ}^{+} m′_{2}

and hence

BV (m_{1}, q) ⊢_{W} m_{1}|_{q} ⟶_{μδ}^{+} m_{2}|_{q}

Using this fact for q = ϵ, we obtain ⊢_{W} m_{1} ⟶_{μδ}^{+} m_{2} which concludes the proof.

Since by theorem 5.4.10 from [32], ⟶_{μδ} is strongly normalizing, we have the following corollary: Corollary 6.3.8. Every sequence of consecutive computation steps is finite.

6.3.4 Shape positions

A structure end step implies modification of the module type M in a manifest specification Eq(m : M) in the translated expressions. In this section we define a congruence identifying expressions which differ only in this way. In the next section we will define a normalisation strategy that preserves this congruence, allowing to prove that structure end steps do not increase the upper bound on the number of evaluation positions. Definition 6.3.2. Let Ψ be an expression and q a position in Ψ. We say that q is a shape position if it is not of the form q′ · 2 · q′′ where Ψ|_{q′} = Eq(ψ : Φ). We denote the set of shape positions of Ψ by Sh(Ψ). Definition 6.3.3. Let ↭ be the least congruence satisfying the condition: if m_{1} ↭ m_{2} then Eq(m_{1} : M_{1}) ↭ Eq(m_{2} : M_{2}) for any M_{1} and M_{2}. If Ψ ↭ Ψ′ we say that they have similar shape.

The notion of similar shape naturally extends to environments. Obviously E ↭ E′ if E = v_{1} : S_{1}; … v_{n} : S_{n} and E′ = v_{1} : S′_{1}; … v_{n} : S′_{n}, and we have S_{1} ↭ S′_{1} … S_{n} ↭ S′_{n}. Lemma 6.3.9. If Ψ ↭ Ψ′ and q is a shape position in Ψ, then:

• q is a shape position in Ψ′;

• Ψ|_{q} ↭ Ψ′|_{q} and BV (Ψ, q) ↭ BV (Ψ′, q);

• if ψ ↭ ψ′ then Ψ[ψ]_{q} ↭ Ψ′[ψ′]_{q};

• if ψ ↭ ψ′ then Ψ{v ↦ ψ} ↭ Ψ′{v ↦ ψ′}; Proof. Easy. Lemma 6.3.10. Let ⊢ Struct Pre_{1} Post_{1} End : M where Pre is evaluated. If

Eval(Pre_{1} || Post_{1}) ⟶ Eval(Pre_{2} || Post_{2})

by a structure end step, then W (Struct Pre_{1} || Post_{1} End) ↭ W (Struct Pre_{2} || Post_{2} End)

Proof. Let m̂_{1} = Struct Pre_{1} || Post_{1} End and m̂_{2} = Struct Pre_{2} || Post_{2} End and let m_{1} = W (m̂_{1}) and m_{2} = W (m̂_{2}). The structure end step moved the mark from the position q′ · i · 2 · n′ to q′ · i′ for i′ = i + 1.

We show by induction on decreasing length of even prefixes q of q′ that m_{1}|_{q} ↭ m_{2}|_{q}. Since q′ · i · 2 · n′ is an evaluation position, its length is odd and the length of q′ is even. We have

m̂_{1}|_{q′} = Struct

\overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1}

… v_{i}▷v_{i} : Ty(M) := Struct StrCs || End … \overline{v}_{l}▷\overline{v}_{l} : S_{l} := P_{l}

End

and

m̂_{2}|_{q′} =Struct

\overline{v}_{1}▷\overline{v}_{1} : S_{1} : P_{1}

… v_{i}▷v_{i} : Ty(SIG(Struct StrCs End)) := Struct StrCs End || … \overline{v}_{l}▷\overline{v}_{l} : S_{l} : P_{l}

End

On the translated side, we have

m_{1}|_{q′} = W (m̂_{1}|_{q′}) = Struct

v_{1}^{1}▷v_{1}^{1} : S_{1}^{1}′ := P_{1}^{1}′ … v_{1}^{k_{1}}▷v_{1}^{k_{1}} : S_{1}^{k_{1}}′ := P_{1}^{k_{1}}′ … v_{i}▷v_{i} : Eq(m′ : W (M)) := m′ … v_{l}^{1}▷v_{l}^{1} : S_{l}^{1}′ := P_{l}^{1}′ … v_{l}^{k_{l}}▷v_{l}^{k_{l}} : S_{l}^{k_{l}}′ := P_{l}^{k_{l}}′

End

and

m_{2}|_{q′} = W (m̂_{2}|_{q′}) = Struct
v_{1}^{1}▷v_{1}^{1} : S_{1}^{1}′ := P_{1}^{1}′ … v_{1}^{k_{1}}▷v_{1}^{k_{1}} : S_{1}^{k_{1}}′ := P_{1}^{k_{1}}′
…

v_{i}▷v_{i} : Eq(m′ : SIG(m′)) := m′

… v_{l}^{1}▷v_{l}^{1} : S_{l}^{1}′ := P_{l}^{1}′ … v_{l}^{k_{l}}▷v_{l}^{k_{l}} : S_{l}^{k_{l}}′ := P_{l}^{k_{l}}′

End

where m′ = W (Struct StrCs || End) = W (Struct StrCs End || )

Both structures m_{1}|_{q′} and m_{2}|_{q′} differ only on position q′ · i · 1 · 2 which is not a shape position, therefore they have similar shape.

For shorter even prefixes q such that q′ = q · j · 2 · q̂ for some j and q̂, we have

m_{1}|_{q} = Struct

v_{1}^{1}▷v_{1}^{1} : S_{1}^{1}′ := P_{1}^{1}′ … v_{1}^{k_{1}}▷v_{1}^{k_{1}} : S_{1}^{k_{1}}′ := P_{1}^{k_{1}}′ … v_{i}▷v_{i} : Eq(m′_{1} : W (M′)) := m′_{1} … v_{l}^{1}▷v_{l}^{1} : S_{l}^{1}′ := P_{l}^{1}′ … v_{l}^{k_{l}}▷v_{l}^{k_{l}} : S_{l}^{k_{l}}′ := P_{l}^{k_{l}}′

End

and

m_{2}|_{q} = Struct

v_{1}^{1}▷v_{1}^{1} : S_{1}^{1}′ := P_{1}^{1}′ … v_{1}^{k_{1}}▷v_{1}^{k_{1}} : S_{1}^{k_{1}}′ := P_{1}^{k_{1}}′ … v_{i}▷v_{i} : Eq(m′_{2} : W (M′)) := m′_{2} … v_{l}^{1}▷v_{l}^{1} : S_{l}^{1}′ := P_{l}^{1}′ … v_{l}^{k_{l}}▷v_{l}^{k_{l}} : S_{l}^{k_{l}}′ := P_{l}^{k_{l}}′

End

where m′_{1} = W (m̂_{1}|_{q·j·2}) and m′_{2} = W (m̂_{2}|_{q·j·2}). The latter two have similar shape by induction hypothesis, since m′_{1} = m_{1}|_{q·j·2} and m′_{2} = m_{2}|_{q·j·2}.

Consequently m_{1}|_{q} ↭ m_{2}|_{q}.

If q is ϵ, we get m_{1} ↭ m_{2} which concludes the proof.

6.3.5 Delta-force reduction

We present here a strategy for the delta-reduction, called delta-force reduction, where the evaluated delta-redex is the smallest possible. In other words a delta-derivation of a delta-force redex does not allow to delta reduce any other redex.

The normalisation strategy announced in the previous section consists in evaluating all μδ_{F} -redexes on shape positions first. Lemma 6.3.12 shows that given two expressions m_{1} and m_{2} such that m_{1} ↭ m_{2}, the μδ_{F} -reductions on shape positions in m_{1} may be simulated in m_{2} in such a way that the equivalence is preserved, i.e. after every reduction step we have m_{1}^{i} ↭ m_{2}^{i}. Once all such redexes are reduced, there are no more μδ-redexes on shape positions. In the next section we prove that further μδ-normalisation preserves the equivalence as well (Lemma 6.3.14).

Definition 6.3.4. Let delta-force reduction ⟶_{δ_{F}} be defined as the smallest congruence such that E ⊢_{W} ϕ ⟶_{δ_{F}} ϕ′ if E ⊢_{δ_{F}} ϕ : Eq(ϕ′ : Ψ) is derivable in the following inference system:

[/S/ACCESS]

E ⊢_{δ_{F}} m : Sig v_{1}▷v_{1} : S_{1} … v_{n}▷v_{n} : S_{n} End
E ⊢_{δ_{F}} m.v_{i} : S_{i}{v_{j} ↦ m.v_{j}}_{j=1...i−1}

[/S/VAR]

\overline{E; v : S; E′ ⊢_{δ_{F}} v : S}

[/S/ABS]

\frac{E ⊢_{δ_{F}} m : M}{E ⊢_{δ_{F}} m : Ty(M)} if m is not a variable or a selection

[/T/ABS]

\frac{E ⊢_{δ_{F}} m : Ty(M)}{E ⊢_{δ_{F}} m : M} if m is a variable or a selection

[M/T/APP]

\frac{E ⊢_{δ_{F}} m_{1} : Funsig(v : M)M′}{E ⊢_{δ_{F}} (m_{1} m_{2}) : M′\{v ↦ m_{2}\}}

[M/T/LAM]

\frac{E; v : Ty(M) ⊢_{δ_{F}} m : M′}{E ⊢_{δ_{F}} Functor[v : M]m : Funsig(v : M)M′}

[M/T/STRUCT]

E ⊢_{δ_{F}} Struct v_{1}▷v_{1} : S_{1} := P_{1} … v_{n}▷v_{n} : S_{n} := P_{n} End : Sig v_{1}▷v_{1} : S_{1} … v_{n}▷v_{n} : S_{n} End

Note that the difference between our definition of ⊢_{δ_{F}} and the definition of ⊢_{δ} in [32] (repeated in this thesis as Definition 1.5.1), lies in the rule [/T/ABS] which replaces the rule [/T/SPEC] allowing both kinds of specifications: Ty(M) and Eq(m : M). We disallow the latter, and hence force the reduction to happen as early as possible.

Lemma 6.3.11. Let Ψ ↭ Ψ′ be two expressions and let q be a shape position.

• If BV (Ψ, q) ⊢_{δ_{F}} Ψ|_{q} : M then BV (Ψ′, q) ⊢_{δ_{F}} Ψ′|_{q} : M′ where M ↭ M′.

• If BV (Ψ, q) ⊢_{δ_{F}} Ψ|_{q} : S then BV (Ψ′, q) ⊢_{δ_{F}} Ψ′|_{q} : S′ where S ↭ S′.

Proof. By mutual induction on the derivation of

BV (Ψ, q) ⊢_{δ_{F}} Ψ|_{q} : M  and  BV (Ψ, q) ⊢_{δ_{F}} Ψ|_{q} : S

If the most recent rule was [/S/VAR], then BV (Ψ, q) = E_{1}; v : S; E_{2} and Ψ|_{q} = v. Since BV (Ψ, q) ↭ BV (Ψ′, q) we have BV (Ψ′, q) = E′_{1}; v : S′; E′_{2} with E_{1} ↭ E′_{1}, S ↭ S′ and E_{2} ↭ E′_{2}. We also have Ψ′|_{q} = v and hence by [/S/VAR] E′_{1}; v : S′; E′_{2} ⊢_{δ_{F}} v : S′.

If the most recent rule was [M/T/STRUCT], we have

Ψ|_{q} = Struct v_{1}▷v_{1} : S_{1} := P_{1} … v_{n}▷v_{n} : S_{n} := P_{n} End

and consequently

Ψ′|_{q} = Struct v_{1}▷v_{1} : S′_{1} := P′_{1} … v_{n}▷v_{n} : S′_{n} := P′_{n} End

with S_{1} ↭ S′_{1} … S_{n} ↭ S′_{n} and P_{1} ↭ P′_{1} … P_{n} ↭ P′_{n}. So using [M/T/STRUCT], we obtain BV (Ψ′, q) ⊢_{δ_{F}} Ψ′|_{q} : Sig v_{1}▷v_{1} : S′_{1} … v_{n}▷v_{n} : S′_{n} End and of course

Sig v_{1}▷v_{1} : S_{1} … v_{n}▷v_{n} : S_{n} End ↭ Sig v_{1}▷v_{1} : S′_{1} … v_{n}▷v_{n} : S′_{n} End

All other rules follow by simple use of induction hypothesis. Let us detail [/S/ACCESS] and [M/T/LAM].

If the most recent rule was [/S/ACCESS], we have q′ = q · i and

BV (Ψ, q′) ⊢_{δ_{F}} m : Sig v_{1}▷v_{1} : S_{1} … v_{n}▷v_{n} : S_{n} End

where m = Ψ|_{q′}. By induction hypothesis,

BV (Ψ′, q′) ⊢_{δ_{F}} m′ : Sig v_{1}▷v_{1} : S′_{1} … v_{n}▷v_{n} : S′_{n} End

where m′ = Ψ|_{q} and Sig v_{1}▷v_{1} : S_{1} … v_{n}▷v_{n} : S_{n} End ↭ Sig v_{1}▷v_{1} : S′_{1} … v_{n}▷v_{n} : S′_{n} End. So we have S_{i} ↭ S′_{i}. Since m ↭ m′ implies m.v_{j} ↭ m′.v_{j} for any j, we also have S_{i}{v_{j} ↦ m.v_{j}}_{j=1..i−1} ↭ S′_{i}{v_{j} ↦ m′.v_{j}}_{j=1..i−1} and using [/S/ACCESS] and the fact that BV (Ψ′, q′) = BV (Ψ′, q), we obtain the desired conclusion.

If the most recent rule was [M/T/LAM], then q′ = q · 2,

BV (Ψ, q′) = BV (Ψ, q); v̂ : Ty(M_{1})  and  Ψ|_{q} = Functor[v̂ : M_{1}]m

By induction hypothesis, BV (Ψ′, q′) ⊢_{δ_{F}} m′ : M′_{2}, where m′ = Ψ|_{q′}, and M_{2} ↭ M′_{2}. Since BV (Ψ, q′) ↭ BV (Ψ′, q′) we have BV (Ψ′, q′) = BV (Ψ′, q); v̂ : Ty(M′_{1}) with M_{1} ↭ M′_{1}. Hence by [M/T/LAM], BV (Ψ′, q) ⊢_{δ_{F}} Functor[v̂ : M′_{1}]m′ : Funsig(v̂ : M′_{1})M′_{2} and obviously Funsig(v̂ : M_{1})M_{2} ↭ Funsig(v̂ : M′_{1})M′_{2} which is the expected conclusion.

Other rules are similar.

Lemma 6.3.12. If m_{1} ↭ m′_{1} and m_{1} ⟶^{q}_{μδ_{F}} m_{2} where q is a shape position, then there

exists m′_{2} such that m′_{1} ⟶^{q}_{μδ_{F}} m′_{2} and m_{2} ↭ m′_{2}.

Proof. Suppose, we have a μ redex at position q in m_{1}. Then m_{1}|_{q} = Functor[v̂ : M]m̂_{1} m̂_{2}. Since m_{1} ↭ m′_{1}, m′_{1}|_{q} = Functor[v̂ : M′]m̂′_{1} m̂′_{2} with M ↭ M′, m̂_{1} ↭ m̂′_{1} and m̂_{2} ↭ m̂′_{2}.

Therefore (Functor[v̂ : M′]m̂′_{1} m̂′_{2}) ⟶_{μ} m̂′_{1}{v̂ ↦ m̂′_{2}} and the latter has similar shape to m̂_{1}{v̂ ↦ m̂_{2}} with is the μ reduct of m_{1}|_{q}.

Consequently, m′_{1} ⟶^{q}_{μ} m′_{2} with m_{2} ↭ m′_{2}.

Suppose, we have a δ_{F} -redex at position q in m_{1}. Then BV (m_{1}, q) ⊢_{δ_{F}} m_{1}|_{q} : Eq(m : M) and by Lemma 6.3.11, BV (m′_{1}, q) ⊢_{δ_{F}} m′_{1}|_{q} : Eq(m′ : M′) with Eq(m : M) ↭ Eq(m′ : M′). Consequently m ↭ m′ and therefore m′_{1} ⟶_{δ_{F}} m′_{1}[m′]_{q} with m_{2} = m_{1}[m]_{q} ↭ m′_{1}[m′]_{q} = m′_{2}.

Lemma 6.3.13. Let m be a module expression without δ_{F} -redexes on shape positions. Then m has no δ-redexes on shape positions.

Proof. Suppose otherwise, that there is a redex at a shape position q. We will show the following fact:

If BV (m, q) ⊢_{δ} m|_{q} : M (or BV (m, q) ⊢_{δ} m|_{q} : S) then BV (m, q) ⊢_{δ_{F}} m|_{q} : M (BV (m, q) ⊢_{δ_{F}} m|_{q} : S) or there is a position q̂ such that q · q̂ is a shape position in m and m|_{q·q̂} is a δ_{F} -redex.

We will prove it by induction on the derivation of the corresponding ⊢_{δ} judgment. The only rule in which the conclusion does not immediately follow from induction

hypothesis is [/S/SPEC] which is replaced by [/S/ABS] in the definition of ⊢_{δ_{F}}.

From the induction hypothesis, we have either a position q̂ such that q · q̂ is a shape position in m and m|_{q·q̂} is a δ_{F} -redex or BV (m, q) ⊢_{δ_{F}} m̂ : S, where m̂ = m|_{q} and S is either Ty(M) or Eq(m′ : M). If there is no position leading to a redex, then S = Ty(M). Otherwise we have a redex at position q̂ = ϵ.

If S = Ty(M) then we can use [/S/ABS] instead of [/S/SPEC] to obtain BV (m, q) ⊢_{δ_{F}} m̂ : M.

Other rules follow immediately from induction hypothesis. Let us take [M/T/LAM] as an example. We have q′ = q·2, m|_{q} = Functor[v̂ : M_{1}]m, BV (m, q′) = BV (m, q); v̂ : Ty(M_{1}) and BV (m, q); v̂ : Ty(M_{1}) ⊢_{δ} m̂ : M_{2}.

From induction hypothesis, we either have a position q̂ in m̂ such that q · 2 · q̂ is a shape position in m and m|_{q·2·q̂} is a δ_{F} -redex or BV (m, q); v̂ : Ty(M_{1}) ⊢_{δ_{F}} m̂ : M_{2}.

In the first case, we have a redex at position 2 · q̂ in Functor[v̂ : M_{1}]m.

In the second case, using [/M/T/LAM], we get BV (m, q) ⊢_{δ_{F}} Functor[v̂ : M_{1}]m : Funsig(v̂ : M_{1})M_{2}.

6.3.6 Counting positions

Before proving that every evaluation sequence is terminating, we define a subset of shape positions, called structural positions, which are very closely related to evaluation positions. By combining the results of the previous sections and confluence of the module reductions in WMC, we show that structural positions of normal forms of translated structures do not change during evaluation. Hence the number of evaluation positions can be bounded and the termination of every evaluation sequence follows easily.

Definition 6.3.5. Let m be a module expression. The set of structural positions of m, denoted by Str(m) is defined as follows:

• ϵ is a structural position in m.

• If q is a structural position in m and m|_{q} is a structure

Struct v_{1}▷v_{1} : S_{1} := P_{1} … v_{n}▷v_{n} : S_{n} := P_{n} End

then q · i · 2 are structural positions for all i = 1 … n.

If we extend Str(·) to terms, by setting Str(t) = {ϵ}, we have the following equality:

Str(Struct v_{1}▷v_{1} : S_{1} := P_{1} … v_{n}▷v_{n} : S_{n} := P_{n} End) = {ϵ} ∪ {1 · 2 · q | q ∈ Str(P_{1})} ∪ {n · 2 · q | q ∈ Str(P_{n})}

Of course all structural positions are shape positions and two module expressions of similar shape have identical sets of structural positions.

Lemma 6.3.14. If m_{1} ↭ m_{2} then the sets of structural positions of μδ-normal forms of m_{1} and m_{2} are equal.

Proof. Note that by Theorem 5.4.10 from [32], all μδ-normal forms of a given module expression are β-equal. Hence their sets of structural positions are equal.

Assume we reduce m_{1} using only μδ_{F} -redexes on shape positions until we obtain m′_{1} without μδ_{F} -redexes on shape positions. Using Lemma 6.3.12 we can copy these reductions in m_{2}, obtaining m′_{2} such that m′_{1} ↭ m′_{2} and m_{2} has no μδ_{F} -redexes on shape positions.

Now, if there is a μδ-redex in m′_{1} by Lemma 6.3.13 the redex is at a non-shape position. Hence the term after reduction has similar shape to m′_{1} and has no redexes at shape positions. If we continue this reduction until a normal form m′′_{1}, we still have m′_{1} ↭ m′′_{1}.

If we μδ-normalize m′_{2}, we obtain m′′_{2} such that m′_{2} ↭ m′′_{2}. Hence m′′_{1} ↭ m′′_{2} and their sets of shape positions are identical.

This also means that their sets of structural positions are identical. Hence structural positions of all normal forms of m_{1} and m_{2} are identical.

Lemma 6.3.15. Given a structure m, the cardinality of the set of evaluation positions of m is smaller than the cardinality of the set of its structural positions multiplied by two.

Proof. Let us denote by #Ev(m) and #Str(m) the cardinality of the set of evaluation and structural positions of m respectively. Let us extend #Ev(·) to term expressions by setting #Ev(t) = 0. Let #Ev(m) = 0 for m a module expression which is not a structure. Then the following equalities are true:

#Ev(Struct v_{1}▷v_{1} : S_{1} := P_{1} … v_{n}▷v_{n} : S_{n} := P_{n} End)
= #Ev(P_{1}) + ⋯ + #Ev(P_{n}) + n + 1
#Str(Struct v_{1}▷v_{1} : S_{1} := P_{1} … v_{n}▷v_{n} : S_{n} := P_{n} End)
= #Str(P_{1}) + ⋯ + #Str(P_{n}) + 1

One can prove by induction on the structure of ψ that

#Ev(ψ) ≤ 2#Str(ψ) − 1

If ψ is a term or a module expression which is not a structure, then the inequality holds, because 0 ≤ 2 × 1 − 1. If ψ is a structure, then by induction hypothesis

#Ev(P_{1}) + ⋯ + #Ev(P_{n}) ≤ 2(#Str(P_{1}) + ⋯ + #Str(P_{n})) − n

Therefore

#Ev(Struct v_{1}▷v_{1} : S_{1} := P_{1} … v_{n}▷v_{n} : S_{n} := P_{n} End) =
= #Ev(P_{1}) + ⋯ + #Ev(P_{n}) + n + 1
≤ 2(#Str(P_{1}) + ⋯ + #Str(P_{n})) − n + n + 1
= 2(#Str(P_{1}) + ⋯ + #Str(P_{n})) + 1
= 2(#Str(P_{1}) + ⋯ + #Str(P_{n}) + 1) − 1
= 2#Str(Struct v_{1}▷v_{1} : S_{1} := P_{1} … v_{n}▷v_{n} : S_{n} := P_{n} End) − 1

Lemma 6.3.16. Given a correct marked structure Struct Pre || Post End, we have the following inequality: #Ev(Struct Pre Post End) ≤ #Ev(W (Struct Pre || Post End)).

Proof. Obvious.

Lemma 6.3.17. Let ⊢ Struct Pre_{1} Post_{1} End : M with Pre_{1} evaluated. If

Eval(Pre_{1} || Post_{1}) ⟶ Eval(Pre_{2} || Post_{2})

by a computation step then Ev(Struct Pre_{1} Post_{1} End) ⊆ Ev(Struct Pre_{2} Post_{2} End) and Ev(W (Struct Pre_{1} || Post_{1} End)) ⊆ Ev(W (Struct Pre_{2} || Post_{2} End))

Proof. Since some module expressions are possibly reduced to a structure, the set of evaluation positions can increase.

Lemma 6.3.18. Let m be a module expression and let m ⟶_{μδ} m′. Then Ev(m) ⊆ Ev(m′).

Proof. Obvious.

Lemma 6.3.19. Let Eval(Pre_{1} || Post_{1}) ⟶ … ⟶ Eval(Pre_{n} || Post_{n}) be an evaluation sequence, where Pre_{1} is evaluated and Struct Pre_{1} Post_{1} End is well-typed in the empty environment.

Then #Ev(m_{1}) ≤ ⋯ ≤ #Ev(m_{n}) < 2#Str(m̂_{1}), where m_{1} = Struct Pre_{1} || Post_{1} End, …, m_{n} = Struct Pre_{n} || Post_{n} End and m̂_{1} is a μδ-normal form of W (m_{1}).

Proof. In this diagram, m′_{i} = W (m_{i}) for i = 1 … n, m′′_{i} is a μδ_{F} -reduced form (by reductions on shape positions) of m′_{i} and m̂_{i} is an μδ-normal form of m′′_{i} (and m′_{i}).

m_{1}  m_{i}  m_{i+1}  m_{j}  m_{j+1}  m_{n}

m′_{1}  m′_{i}  m′_{i+1}  m′_{j}  m′_{j+1}  m′_{n}

m′′_{j}  m′′_{j+1}

m̂_{1}  m̂_{i}  m̂_{i+1}  m̂_{j}  m̂_{j+1}  m̂_{n}

Str(m̂_{1})  Str(m̂_{i}) Str(m̂_{i+1})  Str(m̂_{j}) Str(m̂_{j+1})  Str(m̂_{n})

∗ comp_{∗} str end_{∗} W (·)_{μδ}_{∗}_{μδ}_{+}_{μδ}_{∗}_{μδ}_{∗}_{β}_{∗} ↭_{μδ_{F}}_{∗}_{μδ_{F}}_{∗} ↭_{μδ}_{∗}_{μδ}_{∗} ↭_{μδ}_{∗} = ⋯ = ⋯ = ⋯ =

For simple steps, and structure start steps, m′_{i} = m′_{i+1} and therefore their normal forms are β-equal.

For computation steps m′_{i} =_{μδ} m′_{i+1} by Lemma 6.3.7, and hence their μδ-normal forms are β-equal.

For structure end, m′_{i} ↭ m′_{i+1} by Lemma 6.3.10, and by Lemma 6.3.14, the sets of structural positions of their normal forms are equal.

Hence, Str(m̂_{1}) = ⋯ = Str(m̂_{n}).

The sets of evaluation positions grow to the right and downwards, and hence sizes of all sets of evaluation positions are bounded by 2#Str(m̂_{n}) = 2#Str(m̂_{1}), which is the desired conclusion.

Lemma 6.3.20. Every evaluation sequence starting in a partially evaluated structure, welltyped in the empty environment is finite.

Proof. The position of the mark in the evaluation sequence either does not move (for computation steps) or moves to the right (for other steps).

Since every computation step translates to a non-empty sequence of μδ-reductions, the mark cannot stop at one position forever (Corollary 6.3.5).

On the other hand the number of evaluation positions in any term of the evaluation sequence is bounded by the number of structure positions of the normal form of the first term, multiplied by two (Lemma 6.3.19). Hence the mark cannot move forward forever.

Hence every reduction sequence is finite.

The following closing lemma expresses the whole idea of evaluation. Lemma 3.2.1 from Chapter 3 is its trivial consequence.

Lemma 6.3.21. Given a structure m = Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End such that ⊢ m : M for some module type M, there exists an evaluated structure m′ such that ⊢ m′ : M. Moreover, non-module components of m are unchanged in m′.

Proof. It is enough to evaluate Eval( || \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n}) until a normal form, which is necessarily Eval(\overline{v}_{1}▷\overline{v}_{1} : S′_{1} := P′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S′_{n} := P′_{n} || ). By Lemma 6.1.2, \overline{v}_{1}▷\overline{v}_{1} : S′_{1} := P′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S′_{n} := P′_{n} is evaluated, by Lemma 6.2.3 we have ⊢ Struct \overline{v}_{1}▷\overline{v}_{1} : S′_{1} := P′_{1} … \overline{v}_{n}▷\overline{v}_{n} : S′_{n} := P′_{n} End : M, and since none of evaluation steps changes non-module components, they remain unchanged in the result.

Structure flattening

This chapter is a continuation of the results obtained in Chapter 5 about principal derivations. Its goal is to prove Lemma 3.3.1 from Chapter 3.

7.1 Environments

Lemmas 5.9.1 and 5.9.2 have a conditional form: if the environment is correct after flattening a signature or removing a functor, then any other judgment is also correct. In this section we prove that if the considered module is followed only by classic environment entries, then the environment is indeed correct. We proceed by induction on the length of the environment, inverting well-formedness judgments for specifications, switching from ⊢ to ⊢_{P} and back and using Lemmas 5.9.1 and 5.9.2. For term environment entries this is easy and for block components it sometimes requires extending the environment with more term entries. Therefore we have four lemmas: two for signature flattening and two for functor removal. The first of each pair concerns term environments and the second, all classic environments.

Lemma 7.1.1. Let E_{1}; v : Ty(M); E_{2}; E_{3} ⊢ ok where E_{2} is an environment without modules, E_{3} is a term environment and M = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End. If E_{1}; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}; E_{2}δ ⊢ ok, where δ = \{\overline{v.v_{i}} ↦ \overline{v}_{i}\}, then E_{1}; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}; E_{2}δ; E_{3}δ ⊢ ok.

Proof. By induction on the length of E_{3}. If it is empty, then the conclusion is obvious. Otherwise E_{1}; v : Ty(M); E_{2}; E_{3}; \overline{v}′ : S′ ⊢ ok implies E_{1}; v : Ty(M); E_{2}; E_{3} ⊢ ok and E_{1}; v : Ty(M); E_{2}; E_{3} ⊢ S′ : spec. Let E = E_{1}; v : Ty(M); E_{2}; E_{3} and E′ = E_{1}; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}; E_{2}δ; E_{3}δ. By induction hypothesis we have E′ ⊢ ok.

Since S′ is a term specification S′ = Ty(t) or Eq(e : t). By inversion we have E ⊢ t : s and E ⊢ e : t in case of a manifest specification. In either case we can use Lemmas 5.8.7, 5.9.1 and 5.7.1 to get E′ ⊢ tδ : s or E′ ⊢ eδ : tδ respectively. Now we can use (Ty/Form) or (Eq/Form) to obtain E′ ⊢ S′δ : spec and (Env/Insert) to conclude with E′; v′ : S′δ ⊢ ok.

Lemma 7.1.2. Let E_{1}; v : Ty(M); E_{2} ⊢ ok where E_{2} is a classic environment and M = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End. Then E_{1}; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}; E_{2}δ ⊢ ok, where δ = \{\overline{v.v_{i}} ↦ \overline{v}_{i}\}.

Proof. By induction on the length of E_{2}. If E_{2} is empty then the result follows from the correctness of the signature M. Otherwise E_{1}; v : Ty(M); E_{2}; \overline{v}′ : S′ ⊢ ok implies E_{1}; v : Ty(M); E_{2} ⊢ ok and E_{1}; v : Ty(M); E_{2} ⊢ S′ : spec. Let E = E_{1}; v : Ty(M); E_{2} and E′ = E_{1}; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}; E_{2}δ. By induction hypothesis we have E′ ⊢ ok. Since E_{2}; \overline{v}′ : S′ is classic, S is either a term specification or an abstract block specification.

If S′ is a term specification we just use Lemma 7.1.1.

In case S′ is a block specification Ty(BT), the proof is slightly more complicated. By inversion we obtain E ⊢ BT : blocktype and by Lemma 5.8.7 we get the validity of judgments in Essence_{P} (E ⊢ BT : blocktype). Let us call J this set of judgments. Since by Lemma 5.8.1 every judgment in J is of the form E; E_{3} ⊢_{P} J′ where E_{3} is a term environment, we can apply Lemma 7.1.1 to get E′; E_{3}δ ⊢ ok and Lemma 5.9.1 to get E′; E_{3}δ ⊢_{P} Jδ

Let us call the resulting set of judgments J′. Of course, J′ = Essence_{P} (E′ ⊢ BT δ : blocktype). Since the function Essence_{P} (·) for block correctness judgments is constructed as an inversion of the formation rule, we can use judgments in J′ and the closure property (C3) to get E′ ⊢ BT δ : blocktype.

The closure property assures that (for the case of rewrite specification) if ACC_{E}(E^{Γ}, R) than also ACC_{E′}(E^{Γ}δ, Rδ). Given E′ ⊢ BT δ : blocktype, we use (Ty/Form) to obtain E′ ⊢ S′δ : spec and hence E′; \overline{v}′ : S′δ ⊢ ok by (Env/Insert).

Lemma 7.1.3. Let E_{1}; v : Ty(M); E_{2}; E_{3} ⊢ ok where E_{2} is an environment without modules and E_{3} is a term environment and M = Funsig(v̂ : M_{1})M_{2}. If E_{1}; E_{2} ⊢ ok then E_{1}; E_{2}; E_{3} ⊢

ok.

Proof. By induction on the length of E_{3}, just like in the proof of Lemma 7.1.1. We use Lemma 5.9.2 instead of Lemma 5.9.1.

Lemma 7.1.4. Let E_{1}; v : Ty(M); E_{2} ⊢ ok where E_{2} is a classic environment and M = Funsig(v̂ : M_{1})M_{2}. Then E_{1}; E_{2} ⊢ ok.

Proof. The proof is very similar to the proof of Lemma 7.1.2, but the application of Lemma 5.9.1 is replaced by Lemma 5.9.2 and Lemma 7.1.1 by Lemma 7.1.3. For block specifications we also use the closure property (C4) allowing to deduce ACC_{E_{1};E_{2}}(E^{Γ}, R) from ACC_{E_{1};v:Ty(M);E_{2}}(E^{Γ}, R) (for the case of rewrite specification).

7.2 Structures

This section contains lemmas about sub-structure flattening and functor removal in structures, as well as lemmas for removing other non-classic structure components: module or block components implemented by a path or manifest. The last lemma of this section closes the conservativity proof carried out in Chapters 5, 6 and 7.

Lemma 7.2.1. Let m be an evaluated structure, well-typed in the empty environment. If m is of the form

Struct \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} := P′_{1} … \overline{v′}_{l}▷\overline{v′}_{l} : S′_{l} := P′_{l};

v▷v : Ty(M) := Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End;

\overline{v′′}_{1}▷\overline{v′′}_{1} : S′′_{1} := P′′_{1} … \overline{v′′}_{k}▷\overline{v′′}_{k} : S′′_{k} := P′′_{k} End

where \overline{v′′}_{1}▷\overline{v′′}_{1} : S′′_{1} := P′′_{1} … \overline{v′′}_{k}▷\overline{v′′}_{k} : S′′_{k} := P′′_{k} are classic components, then the structure m′ equal

Struct \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} := P′_{1} … \overline{v′}_{l}▷\overline{v′}_{l} : S′_{l} := P′_{l};

\overline{~ v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{~ v}_{n}▷\overline{v}_{n} : S_{n} := P_{n};

\overline{v′′}_{1}▷\overline{v′′}_{1} : S′′_{1}δ := P′′_{1}δ … \overline{v′′}_{k}▷\overline{v′′}_{k} : S′′_{k}δ := P′′_{k}δ End

where δ = \{\overline{v.v_{i}} ↦ \overline{v}\}, and \{\overline{~ v}_{1} … \overline{~ v}_{n}\} are new labels different from \{\overline{v′}_{1} … \overline{v′}_{l}, \overline{v′′}_{1} … \overline{v′′}_{k}\}, is also an evaluated structure, well-typed in the empty environment.

Proof. If m is a well-typed structure then by inversion, its primary signature is well-typed and hence the environment

\overline{v′}_{1} : S′_{1} … \overline{v′}_{l} : S′_{l}; \overline{v} : Ty(M); \overline{v′′}_{1} : S′′_{1} … \overline{v′′}_{k} : S′′_{k}

is correct. We also know that

• \overline{v′}_{1} : S′_{1} … \overline{v′}_{i−1} : S′_{i−1} ⊢ P′_{i} : S′_{i} for i ≤ m

• \overline{v′}_{1} : S′_{1} … \overline{v′}_{l} : S′_{l} ⊢ Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End : M

• \overline{v′}_{1} : S′_{1} … \overline{v′}_{l} : S′_{l}; \overline{v} : Ty(M); \overline{v′′}_{1} : S′′_{1} … \overline{v′′}_{j−1} : S′′_{j−1} ⊢ P′′_{j} : S′′_{j} for j ≤ k

Since m is evaluated, we know also that M = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End. In order to show that m′ is well-typed, we first need to know that the environment

\overline{v′}_{1} : S′_{1} … \overline{v′}_{l} : S′_{l}; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}; \overline{v′′}_{1} : S′′_{1}δ … \overline{v′′}_{k} : S′′_{k}δ

is correct. This follows directly from Lemma 7.1.2. Consequently, the primary signature of m′ is correct. We are left to prove that the implementations of components of m′ verify their respective specifications. From the first point above we know that

• \overline{v′}_{1} : S′_{1} … \overline{v′}_{i−1} : S′_{i−1} ⊢ P′_{i} : S′_{i} for i ≤ m From the second point we know that components of

Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} End

are correct. This gives us:

• \overline{v′}_{1} : S′_{1} … \overline{v′}_{l} : S′_{l}; \overline{v}_{1} : S_{1} … \overline{v}_{i−1} : S_{i−1} ⊢ P_{i} : S_{i} for i ≤ n

We are left with the last m components. We have to show that

• \overline{v′}_{1} : S′_{1} … \overline{v′}_{l} : S′_{l}; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}; \overline{v′′}_{1} : S′′_{1}δ … \overline{v′′}_{j−1} : S′′_{j−1}δ ⊢ P′′_{j}δ : S′′_{j}δ for j ≤ k

Let us pick an arbitrary j. Since S′′_{j} is classic, it is either a term specification or a block specification. If it is a term specification, then by Lemmas 5.8.7, 5.9.1 and 5.7.1, we get the desired conclusion.

If S′′ is a block specification then it is of the form Ty(BT) We already know that S′′_{j}δ is a correct specification and hence

\overline{v′}_{1} : S′_{1} … \overline{v′}_{l} : S′_{l}; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}; \overline{v′′}_{1} : S′′_{1}δ … \overline{v′′}_{j−1} : S′′_{j−1}δ ⊢ BT δ : blocktype

If BT is an inductive specification IndSpec(E^{I} := E^{C}), then P′′_{j} is necessarily Ind(E^{I} := E^{C}). Since IndSpec(E^{I} := E^{C})δ is correct, then we can use the rule (IndSpec/Ind) to get the desired conclusion.

If BT is a rewrite specification RewSpec(E^{Γ}, R), then P′′_{j} is necessarily Rew(E^{Γ}, R). Like previously, we can apply the rule (RewSpec/Rew), because the side-condition is satisfied. Indeed, by the closure property (C3), we can deduce COMP_{E′}(E^{Γ}δ, Rδ) from COMP_{E}(E^{Γ}, R), where E and E′ are

\overline{v′}_{1} : S′_{1} … \overline{v′}_{l} : S′_{l}; v : Ty(M); \overline{v′′}_{1} : S′′_{1} … \overline{v′′}_{j−1} : S′′_{j−1}

and

\overline{v′}_{1} : S′_{1} … \overline{v′}_{l} : S′_{l}; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}; \overline{v′′}_{1} : S′′_{1}δ … \overline{v′′}_{j−1} : S′′_{j−1}δ

respectively.

Since j was arbitrary we now have all premises of the (Sig/Struct) rule allowing to derive the well-typedness of the structure m′ in the empty environment.

Lemma 7.2.2. Let m be an evaluated structure, well-typed in the empty environment. If m is of the form

Struct \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} := P′_{1} … \overline{v′}_{l}▷\overline{v′}_{l} : S′_{l} := P′_{l};

v▷v : Ty(Funsig(v̂ : M_{1})M_{2}) := m;

\overline{v′′}_{1}▷\overline{v′′}_{1} : S′′_{1} := P′′_{1} … \overline{v′′}_{k}▷\overline{v′′}_{k} : S′′_{k} := P′′_{k} End where \overline{v′′}_{1}▷\overline{v′′}_{1} : S′′_{1} := P′′_{1} … \overline{v′′}_{k}▷\overline{v′′}_{k} : S′′_{k} := P′′_{k} are classic components, then the structure m′ equal

Struct \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} := P′_{1} … \overline{v′}_{l}▷\overline{v′}_{l} : S′_{l} := P′_{l};

\overline{v′′}_{1}▷\overline{v′′}_{1} : S′′_{1} := P′′_{1} … \overline{v′′}_{k}▷\overline{v′′}_{k} : S′′_{k} := P′′_{k} End

is also an evaluated structure, well-typed in the empty environment.

Proof. The proof of this lemma is very similar to the proof of Lemma 7.2.1. We use Lemma 7.1.4 to deduce that the primary signature of m′ is well-typed. To get the fact that all implementations satisfy their respective specifications, for term components we use Lemmas 5.8.7, 5.9.2 and 5.7.1 and for block components, inductive definitions are handled trivially and correctness of definitions by rewriting follows from the closure property (C4), allowing to deduce COMP_{E′}(E^{Γ}, R) from COMP_{E}(E^{Γ}, R), where E and E′ are

\overline{v′}_{1} : S′_{1} … \overline{v′}_{l} : S′_{l}; v : Ty(Funsig(v̂ : M_{1})M_{2}); \overline{v′′}_{1} : S′′_{1} … \overline{v′′}_{j−1} : S′′_{j−1}

and

\overline{v′}_{1} : S′_{1} … \overline{v′}_{l} : S′_{l}; \overline{v′′}_{1} : S′′_{1} … \overline{v′′}_{j−1} : S′′_{j−1}

respectively.

Lemma 7.2.3. Let m be an evaluated structure, well-typed in the empty environment. If m is of the form

Struct \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} := P′_{1} … \overline{v′}_{l}▷\overline{v′}_{l} : S′_{l} := P′_{l};

\overline{v}▷\overline{v} : Eq(\overline{p} : Ψ) := \overline{ϕ};

\overline{v′′}_{1}▷\overline{v′′}_{1} : S′′_{1} := P′′_{1} … \overline{v′′}_{k}▷\overline{v′′}_{k} : S′′_{k} := P′′_{k} End

where \overline{v′′}_{1}▷\overline{v′′}_{1} : S′′_{1} := P′′_{1} … \overline{v′′}_{k}▷\overline{v′′}_{k} : S′′_{k} := P′′_{k} are classic components, and \overline{v}▷\overline{v} : Eq(\overline{p} : Ψ) := \overline{ϕ} is either a block component or a module component then the structure m′ equal

Struct \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} := P′_{1} … \overline{v′}_{l}▷\overline{v′}_{l} : S′_{l} := P′_{l};

\overline{v′′}_{1}▷\overline{v′′}_{1} : S′′_{1}δ := P′′_{1}δ … \overline{v′′}_{k}▷\overline{v′′}_{k} : S′′_{k}δ := P′′_{k}δ End

where δ = \{\overline{v} ↦ \overline{p}\} is also an evaluated structure, well-typed in the empty environment.

Proof. This is an easy consequence of Lemma 4.1.5.

Lemma 7.2.4. Let m be an evaluated structure, well-typed in the empty environment. If m is of the form

Struct \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} := P′_{1} … \overline{v′}_{l}▷\overline{v′}_{l} : S′_{l} := P′_{l};

\overline{v}▷\overline{v} : Ty(Ψ) := \overline{p};

\overline{v′′}_{1}▷\overline{v′′}_{1} : S′′_{1} := P′′_{1} … \overline{v′′}_{k}▷\overline{v′′}_{k} : S′′_{k} := P′′_{k} End

where \overline{v′′}_{1}▷\overline{v′′}_{1} : S′′_{1} := P′′_{1} … \overline{v′′}_{k}▷\overline{v′′}_{k} : S′′_{k} := P′′_{k} are classic components, and \overline{v}▷\overline{v} : Ty(Ψ) := \overline{p} is either a block component or a module component, then the structure m′ equal

Struct \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} := P′_{1} … \overline{v′}_{l}▷\overline{v′}_{l} : S′_{l} := P′_{l};

\overline{v′′}_{1}▷\overline{v′′}_{1} : S′′_{1}δ := P′′_{1}δ … \overline{v′′}_{k}▷\overline{v′′}_{k} : S′′_{k}δ := P′′_{k}δ End

where δ = \{\overline{v} ↦ \overline{p}\} is also an evaluated structure, well-typed in the empty environment.

Proof. This is again an easy consequence of Lemma 4.1.5.

The following lemma is a formal counter-part of Lemma 3.3.1, presented in Chapter 3.

Lemma 7.2.5. Let m be an evaluated structure, well-typed in the empty environment. If m is of the form

Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n};

\overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} := P′_{1} … \overline{v′}_{k}▷\overline{v′}_{k} : S′_{k} := P′_{k}

v▷v : Ty(t) := e; End

where \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n} are classic components and t is typable is the environment \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}, then there exists a classic structure m′, well-typed in the empty environment, and of the following form:

Struct \overline{v}_{1}▷\overline{v}_{1} : S_{1} := P_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} := P_{n};

\overline{v′′}_{1}▷\overline{v′′}_{1} : S′′_{1} := P′′_{1} … \overline{v′′}_{l}▷\overline{v′′}_{l} : S′′_{l} := P′′_{l}

v▷v : Ty(t) := e′; End

Proof. We proceed from right to left to eliminate all non-classic components of m.

Let us consider the last non-classic component. It is either a block component or a module component. If it contains a manifest specification, then we use Lemma 7.2.3. If it is an abstract module or block component, then if its implementation is a path or a sequence of paths, we use Lemma 7.2.4. If it is an abstract module component then its implementation can also be a functor or an evaluated structure. In the first case we use Lemma 7.2.2, and in the second case, Lemma 7.2.1.

If it is an abstract block component then it can be implemented by a basic block which does not match exactly its specification, for example \overline{v}▷\overline{v} : Ty(IndSpec(E^{I}′ := E^{C}′)) := Ind(E^{I} := E^{C}). In this case the primary block type of the basic block is correct and convertible with the block type in the specification. Hence by Lemma 4.1.7 we can simply change the specification obtaining the classic component. In our example, we would get \overline{v}▷\overline{v} : Ty(IndSpec(E^{I} := E^{C})) := Ind(E^{I} := E^{C}).

In all cases the total number of components and sub-components of m decreases and therefore the procedure is necessarily finite. Note also, that substitutions appearing in lemmas used in the proof do not change the type of the last component t, as the domains of the substitutions contain names of non-classic components of m, which all appear in m after the index n. Since t is typable in the environment \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} it cannot contain names of non-classic components and therefore none of the substitutions can change it.

Decidability

In this chapter we prove that decidability of the calculus with inductive types and definitions by rewriting implies decidability of the calculus with named modules.

The most important part consists in showing that type-checking terms in an arbitrary environment can be reduced in polynomial time into type-checking terms in a classic environment, i.e. without modules and manifest blocks. We proceed in two steps: first we show how to remove modules, and then manifest blocks.

As far as the first step is considered we reuse signature flattening and functor removal operations defined in the previous chapters. It is easy to show that these operations are bidirectional, i.e. a term judgment holds after the operation if and only if it holds before the operation. Unfortunately this is not the case for the treatment of manifest modules, presented in Chapter 7. Therefore we introduce a more complex way of eliminating manifest modules, which is bidirectional.

This chapter is organised as follows. Section 8.1 shows that a sequence of paths satisfies an inductive specification in ⊢ only if it does in ⊢_{P}. This result is later used to show decidability of satisfaction of inductive specifications and to show that all modules can be removed from the environment before the manifest blocks. Note that in Chapter 7 removing of both these kinds of components is done simultaneously.

Section 8.2 presents the bidirectional way of eliminating manifest modules. Section 8.3 and 8.4 contain all lemmas needed to show the correctness of respectively the first and the second phase of the reduction: removing of modules and removing of manifest blocks. Section 8.5 completes these results with the brief description of the module typing algorithm given in the proof of Theorem 2.

8.1 Principal derivations of inductive types

This section shows that a sequence of paths can satisfy an inductive specification only if it is declared as an inductive block, i.e. the satisfaction of an inductive specification is derivable in ⊢_{P}.

The proof of this property follows very closely the reasoning presented in Section 5.8, starting by the definition below which is very similar to Definition 5.8.2, but much simpler.

Definition 8.1.1. The inductive essence of a judgment E ⊢ J, written IndEss(E ⊢ J) is defined as follows:

IndEss(E ⊢ \overline{p} : IndSpec(E^{I} := E^{C})) = \{E ⊢_{P} \overline{p} : IndSpec(E^{I}′ := E^{C}′)\} ∪ \{E ⊢ IndSpec(E^{I} := E^{C}) ≈ IndSpec(E^{I}′ := E^{C}′)\}

for some E^{I}′ and E^{C}′

IndEss(E ⊢ p : Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End) = ⋃\{IndEss(E ⊢ \overline{p.v_{i}} : S_{i}\{\overline{v}_{j} ↦ \overline{p.v_{j}}\}) for i = 1 … n\}

IndEss(E ⊢ Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End <: Sig \overline{v′}_{1}▷\overline{v′}_{1} : S′_{1} … \overline{v′}_{m}▷\overline{v′}_{m} : S′_{m} End) = ⋃\{IndEss(E; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n} ⊢ \overline{v}′_{i} : S′_{i}) for i = 1 … m\} where \{\overline{v}_{1} … \overline{v}_{n}\} ⊇ \{\overline{v}′_{1} … \overline{v}′_{m}\} and v_{i}^{j} = v′_{i′}^{j′} implies v_{i}^{j} = v′_{i′}^{j′}

IndEss(E ⊢ \overline{p} : Ty(Ψ)) = IndEss(E ⊢ \overline{p} : Ψ)

IndEss(E ⊢ \overline{p} : Eq(\overline{ϕ} : Ψ)) = IndEss(E ⊢ \overline{p} : Ψ)

IndEss(E ⊢ J) = ∅ otherwise

The lemmas below correspond to Lemmas 5.8.1, 5.8.2, 5.8.4, 5.8.5 and 5.8.7 respectively.

Lemma 8.1.1. For any judgment E ⊢ J, we have

IndEss(E ⊢ J) = IndEss(E; E′_{1} ⊢ \overline{p}_{1} : IndSpec(E_{1}^{I} := E_{1}^{C})) ∪ …
⋯ ∪ IndEss(E; E′_{n} ⊢ \overline{p}_{n} : IndSpec(E_{n}^{I} := E_{n}^{C}))

for some \overline{p}_{1} … \overline{p}_{n}, E_{1}^{I} … E_{n}^{I} and E_{1}^{C} … E_{n}^{C}.

Moreover this equality is stable under substitution, i.e. if E = E_{1}; v : S; E_{2} and given a substitution δ = {v ↦ p}, we have

IndEss(E_{1}; E_{2}δ ⊢ Jδ) = IndEss(E_{1}; E_{2}δ; E′_{1}δ ⊢ \overline{p}_{1}δ : IndSpec(E_{1}^{I}δ := E_{1}^{C}δ)) ∪ …
⋯ ∪ IndEss(E_{1}; E_{2}δ; E′_{n}δ ⊢ \overline{p}_{n}δ : IndSpec(E_{n}^{I}δ := E_{n}^{C}δ))

Proof. By inspection of the definition of IndEss(·).

Lemma 8.1.2. If E ⊢_{P} J then all judgments in IndEss(E ⊢ J) are true.

Proof. For judgments of the form E ⊢_{P} \overline{p} : IndSpec(E^{I} := E^{C}), we get the conclusion by reflexivity of ≈ (Lemma 4.1.12).

For other judgments of the form ψ : Φ, we proceed by induction on the weight of Φ (see Definition 5.8.1)

For all other judgments in ⊢_{P} the conclusion is trivial.

Lemma 8.1.3. If E_{1}; \overline{v} : S; E_{2} ⊢_{P} J and E_{1} ⊢ \overline{p} : S and all judgments in IndEss(E_{1} ⊢ \overline{p} : S) hold, then all judgments in IndEss(E_{1}; E_{2}δ ⊢ Jδ) hold as well, where δ = \{\overline{v} ↦ \overline{p}\}.

Proof. The proof is almost identical to the proof of Lemma 5.8.4. We proceed by induction on the derivation of E_{1}; \overline{v} : S; E_{2} ⊢_{P} J.

If the last rule of the derivation was (P/Env/Lookup) and the path that was looked up was \overline{v} then we use the assumption. Otherwise we can apply the rule (P/Env/Lookup) to the correct environment E_{1}; E_{2}δ and get E_{1}; E_{2}δ ⊢_{P} \overline{v}′ : S′δ and all judgments in IndEss(E_{1}; E_{2}δ ⊢_{P} \overline{v}′ : S′δ) by Lemma 8.1.2.

The other interesting case is the rule (P/Sig/Access), which is handled exactly as in Lemma 5.8.4.

The rest of the rules can be handled by induction hypothesis.

Lemma 8.1.4. Let E_{1}; \overline{v} : S; E_{2} ⊢ J and let all judgments in IndEss(E_{1}; \overline{v} : S; E_{2} ⊢ J) hold. If E_{1} ⊢ \overline{p} : S and all judgments in IndEss(E_{1} ⊢ \overline{p} : S) hold, then all judgments in IndEss(E_{1}; E_{2}δ ⊢ Jδ) also hold for δ = \{\overline{v} ↦ \overline{p}\}.

Proof. Given Lemma 8.1.1, it is enough to show the above for judgments of the form E_{1}; \overline{v} : S; E_{2}; E′ ⊢ \overline{p} : IndSpec(E^{I} := E^{C}). By Definition 8.1.1,

IndEss(E_{1}; \overline{v} : S; E_{2}; E′ ⊢ \overline{p} : IndSpec(E^{I} := E^{C})) =
\{E_{1}; \overline{v} : S; E_{2}; E′ ⊢_{P} \overline{p} : IndSpec(E^{I}′ := E^{C}′)\} ∪

\{E_{1}; \overline{v} : S; E_{2}; E′ ⊢ IndSpec(E^{I} := E^{C}) ≈ IndSpec(E^{I}′ := E^{C}′)\}

for some E^{I}′ and E^{C}′. By Lemma 8.1.3, we have the validity of judgments in

IndEss(E_{1}; E_{2}δ; E′δ ⊢ \overline{p}δ : IndSpec(E^{I}′δ := E^{C}′δ))

i.e.

E_{1}; E_{2}δ; E′δ ⊢_{P} \overline{p}δ : IndSpec(E^{I}′′ := E^{C}′′)  and  (8.1)
E_{1}; E_{2}δ; E′δ ⊢ IndSpec(E^{I}′δ := E^{C}′δ) ≈ IndSpec(E^{I}′′ := E^{C}′′)

for some E^{I}′′ and E^{C}′′. By Lemma 4.1.5 we also have

E_{1}; E_{2}δ; E′δ ⊢ IndSpec(E^{I}δ := E^{C}δ) ≈ IndSpec(E^{I}′δ := E^{C}′δ)

which by transitivity of conversion (Lemma 4.1.12) gives

E_{1}; E_{2}δ; E′δ ⊢ IndSpec(E^{I}δ := E^{C}δ) ≈ IndSpec(E^{I}′′ := E^{C}′′)  (8.2)

Putting (8.1) and (8.2) together, we have proved the validity of the judgments in

IndEss(E_{1}; E_{2}δ; E′δ ⊢ \overline{p}δ : IndSpec(E^{I}δ := E^{C}δ))

which concludes the proof.

Lemma 8.1.5. If E ⊢ J then all judgments in IndEss(E ⊢ J) hold. In particular, if E ⊢ \overline{p} : IndSpec(E^{I} := E^{C}) then

E ⊢_{P} \overline{p} : IndSpec(E^{I}′ := E^{C}′) and E ⊢ IndSpec(E^{I} := E^{C}) ≈ IndSpec(E^{I}′ := E^{C}′)

for some E^{I}′ and E^{C}′.

Proof. The proof is done by induction on the derivation of E ⊢ J and is very similar to the proof of Lemma 5.8.7.

If the last rule used to derive E ⊢ J was (Env/Lookup), then the judgment E ⊢_{P} J can be derived using (P/Env/Lookup) and we can use Lemma 8.1.2.

For term rules we have nothing to prove.

For module rules, the most interesting rule is (Subsumption) for the case when m is a path and M_{1} and M_{2} are signatures. We proceed exactly like in Lemma 5.8.7, using Lemma 8.1.4.

The rules (Sig/Access), (Sig/Sub) and (Strengthening) are handled immediately by induction hypothesis and for other module rules, there is nothing to prove.

Specification rules (Ty/Sat), (Ty/Type) and (Eq/Sat), (Eq/Type) are handled by induction hypothesis and for others there is nothing to prove.

For block rules, the only interesting one is (Block/Conv), for the case when B is a sequence of paths \overline{p} and BT and BT′ are both inductive specifications:

\frac{E ⊢ \overline{p} : IndSpec(E^{I} := E^{C}) E ⊢ IndSpec(E^{I}′ := E^{C}′) : blocktype E ⊢ IndSpec(E^{I} := E^{C}) ≈ IndSpec(E^{I}′ := E^{C}′)}{E ⊢ \overline{p} : IndSpec(E^{I}′ := E^{C}′)}

By induction hypothesis, we get

E ⊢_{P} \overline{p} : IndSpec(E^{I}′′ := E^{C}′′) and E ⊢ IndSpec(E^{I} := E^{C}) ≈ IndSpec(E^{I}′′ := E^{C}′′)

for some E^{I}′′ and E^{C}′′. From the second judgment and the third premise, symmetry and transitivity (Lemma 4.1.12), we get

E ⊢ IndSpec(E^{I}′ := E^{C}′) ≈ IndSpec(E^{I}′′ := E^{C}′′)

which concludes the proof since there is nothing to prove for convertibility rules.

8.2 Elimination of manifest modules

We show that it is possible to transform a manifest module component v : Eq(p : M) into an abstract module component v : Ty(M/p), provided that it is the last module component in the environment. The lemmas in this section correspond to Lemmas 5.9.1, 7.1.1 and 7.1.2 for signature flattening and to Lemmas 5.9.2, 7.1.3 and 7.1.4 for functor removal.

The first lemma is a bit more complex than its counterparts (Lemmas 5.9.1 and 5.9.2), because we have to take care of equalities resulting from v ≈ p, derivable in the initial environment, but not in the environment after the elimination operation. That is why we need the following auxiliary definition.

Definition 8.2.1. Given a correct environment E and a module path p, The set of labels of the path p in the environment E, denoted by Lab_{E}(p), is defined as follows: if E ⊢_{P} p : M_{P} then

• if M_{P} = Funsig(v̂ : M_{1})M_{2} then Lab_{E}(p) = ∅

• if M_{P} = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End then Lab_{E}(p) = \{\overline{v}_{1} … \overline{v}_{n}\}

Lemma 8.2.1. Let E = E_{1}; v : Eq(p : M); E_{2} and E′ = E_{1}; v : Ty(M/p); E_{2} where E_{2} is an environment without modules. If E ⊢_{P} J and E′ ⊢ ok then J does not contain subexpressions Eq(v : M̂) and

• E′ ⊢ p ≈ p′ and E′ ⊢ v.v_{i}^{j} ≈ p′.v_{i}^{j} for all v_{i}^{j} ∈ Lab_{E}(v) ∩ Lab_{E}(p′)

if J is of the form v ≈ p′ or p′ ≈ v for p′ ≠ v

• nothing if J = v : Eq(p : M) or v ≈ v

• E′ ⊢ J otherwise

Proof. We start by proving the following two facts:

• Lab_{E}(v) ⊆ Lab_{E}(p)

• for all v_{i}^{j} ∈ Lab_{E}(v) we have E′ ⊢ v.v_{i}^{j} ≈ p.v_{i}^{j}

The first statement follows by the analysis of the module types of p. By inversion we get E ⊢ p : M and therefore by Lemma 5.6.5, E ⊢ M_{P} /p <: M, where E ⊢_{P} p : M_{P}. So either both Mp and M are functor types and Lab_{E}(v) = Lab_{E}(p) = ∅ or they are both signatures. It this case obviously M_{P} has more labels than M.

The second statement is trivial if M is a functor type. Otherwise

M = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End

and

M/p = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1}/\overline{p.v_{1}} … \overline{v}_{n}▷\overline{v}_{n} : S_{n}/\overline{p.v_{n}} End

and Since E_{1}; v : Ty(M/p) ⊢ p : M and E_{1}; v : Ty(M/p) ⊢ M : modtype, by Lemma 4.1.14 we also have

E_{1}; v : Ty(M/p); \overline{v}_{1} : S_{1}/\overline{p.v_{1}} … \overline{v}_{i} : S_{i}/\overline{p.v_{i}} ⊢ v_{i}^{j} ≈ p.v_{i}^{j}

for all i = 1 … n and any j. Since E_{1}; v : Ty(M/p) ⊢ v.v_{i} : S_{i}/\overline{p.v_{i}}\{v_{j} ↦ \overline{v.v_{j}}\}, by Lemma 4.1.6 we get

E_{1}; v : Ty(M/p) ⊢ v.v_{i}^{j} ≈ p.v_{i}^{j}

Now we can do the proof by induction on the derivation of E ⊢_{P} J.

If the last rule in the derivation is (P/Env/Lookup), there are two cases to consider. Either the sequence of variables which is looked up is different from v or the variable which is looked up is v. In the first case we can do the same in E′ and the absence of subexpressions Eq(v : M̂) follows from the v does not appear in E_{1} and E_{2} has no module components. In the second case, we do not have to prove anything.

If the last rule is (P/Sig/Access) or (P/Ty/Type), then it simply follows by induction hypothesis.

To analyze the rule (P/Eq/Type) we have to consider two cases. If the premise is different from v : Eq(p : M) we can proceed by induction hypothesis. If the premise is v : Eq(p : M), then we have to show E′ ⊢ v : M. But this holds, since E_{1} ⊢ p : M implies E_{1} ⊢ M/p <: M by Lemma 4.1.14 and since E′ is an extension of E_{1}, we can apply (Subsumption) in order to get E′ ⊢ v : M from E′ ⊢ v : M/p.

If the last rule was (P/Eq/Comp) then either the premise is different from v : Eq(p : M) and we can proceed by induction hypothesis or it is v : Eq(p : M). In the latter case we already have E′ ⊢ v.v_{i}^{j} ≈ p.v_{i}^{j} for all v_{i}^{j} ∈ Lab_{E}(v) and since Lab_{E}(v) ⊆ Lab_{E}(p), we have Lab_{E}(v) ∩ Lab_{E}(p) = Lab_{E}(v).

If the last rule was a term rule, then we can simply apply induction hypothesis, using Lemmas 5.7.3 and 5.7.4 to establish the correctness of bigger environments in rules (P/Term/Prod) and (P/Term/Abs). In the rule (P/Term/Conv) we cannot have E ⊢ v ≈ p′ as a premise since v is a module path.

If block rules were used, then we simply apply induction hypothesis.

Conversion rules for paths have to be carefully studied, particularly the transitivity rule:

\frac{E ⊢ p′ ≈ p′′ E ⊢ p′′ ≈ p′′′}{E ⊢ p′ ≈ p′′′}

We will consider four cases depending on which of p′, p′′, p′′′ are equal v.

• If none of p′, p′′, p′′′ is equal v then we proceed by induction hypothesis.

• If two or three of p′, p′′, p′′′ are equal v then either we do not have to prove anything or the conclusion is one of the premises and we proceed by induction hypothesis.

• If one of p′, p′′, p′′′ is equal v then the cases p′ = v and p′′′ = v are symmetric. We present the second one. By induction hypothesis, we have E′ ⊢ p′ ≈ p′′ and E′ ⊢ p′′ ≈ p so using transitivity, we get E′ ⊢ p′ ≈ p.

We also have to prove E′ ⊢ v.v_{i}^{j} ≈ p′.v_{i}^{j} for all v_{i}^{j} ∈ Lab_{E}(v) ∩ Lab_{E}(p′). We will use p again.

Note that for all v_{i}^{j} ∈ Lab_{E}(p) ∩ Lab_{E}(p′) both p.v_{i}^{j} and p′.v_{i}^{j} are well-typed.

Hence we can use the rule (Conv/Path) to show E′ ⊢ p.v_{i}^{j} ≈ p′.v_{i}^{j} for all v_{i}^{j} ∈ Lab_{E}(p) ∩ Lab_{E}(p′) using E′ ⊢ p ≈ p′, which we have already proved.

Now, using the facts proved in the beginning of the proof and transitivity, we can get E′ ⊢ v.v_{i}^{i} ≈ p′.v_{i}^{j} for all v_{i}^{j} ∈ Lab_{E}(v) ∩ Lab_{E}(p′).

• If p′′ = v, then by induction hypothesis E′ ⊢ p′ ≈ p and E′ ⊢ p ≈ p′′′ so using transitivity, we get E′ ⊢ p′ ≈ p′′′.

The next interesting rule is (P/Conv/Path) if exactly one of concerned paths is v. We have

\frac{E ⊢_{P} v.v_{i}^{j} : Ψ E ⊢_{P} v ≈ p′ E ⊢_{P} p′.v_{i}^{j} : Ψ′}{E ⊢_{P} v.v_{i}^{j} ≈ p′.v_{i}^{j}}

Since both v.v_{i}^{j} and p′.v_{i}^{j} are typable we have v_{i}^{j} ∈ Lab_{E}(v) ∩ Lab_{E}(p′) and by induction hypothesis about the second judgment, we get the desired conclusion.

If none of the premises is v, then we simply proceed by induction hypothesis. Other convertibility rules are also handled by induction hypothesis since none of the convertibility premises contains v as they are all term judgments.

Lemma 8.2.2. Let E_{1}; v : Eq(p : M); E_{2}; E_{3} ⊢ ok where E_{2} is an environment without modules and E_{3} is a term environment. If E_{1}; v : Ty(M/p); E_{2} ⊢ ok then E_{1}; v : Ty(M/p); E_{2}; E_{3} ⊢ ok.

Proof. By induction on the length of E_{3}. If it is empty, then the conclusion is obvious.

Otherwise E_{1}; v : Eq(p : M); E_{2}; E_{3}; \overline{v}′ : S′ ⊢ ok implies E_{1}; v : Eq(p : M); E_{2} ⊢ ok and E_{1}; v : Eq(p : M); E_{2}; E_{3} ⊢ S′ : spec. Let E = E_{1}; v : Eq(p : M); E_{2}; E_{3} and E′ = E_{1}; v : Ty(M/p); E_{2}; E_{3}. By induction hypothesis we have E′ ⊢ ok.

Since S′ is a term specification S′ = Ty(t) or Eq(e : t). By inversion we have E ⊢ t : s and E ⊢ e : t in case of a manifest specification. In either case we can use Lemmas 5.8.7 and 8.2.1 to get E′ ⊢ t : s or E′ ⊢ e : t respectively. Now we can use (Ty/Form) or (Eq/Form) to obtain E′ ⊢ S′ : spec and (Env/Insert) to conclude with E′; v′ : S′ ⊢

ok.

The lemma below corresponds to Lemmas 7.1.2 and 7.1.4. In fact it is stronger than the latter two, since they are valid only for a classic E_{2} (i.e. without modules and without manifest blocks).

Lemma 8.2.3. Let E_{1}; v : Eq(p : M); E_{2} ⊢ ok where E_{2} is an environment without modules. Then E_{1}; v : Ty(M/p); E_{2} ⊢ ok.

Proof. By induction on the length of E_{2}. If it is empty, then E_{1} ⊢ p : M and hence E_{1} ⊢ M/p : modtype and E_{1}, v : Ty(M/p) ⊢ ok.

Otherwise E_{1}; v : Eq(p : M); E_{2}; \overline{v}′ : S′ ⊢ ok implies E_{1}; v : Eq(p : M); E_{2} ⊢ ok and E_{1}; v : Eq(p : M); E_{2} ⊢ S′ : spec. Let E = E_{1}; v : Eq(p : M); E_{2} and E′ = E_{1}; v : Ty(M/p); E_{2}. By induction hypothesis we have E′ ⊢ ok. Since S′ is not a module specification, it is either a term specification or a block specification.

If S′ is a term specification then we can use Lemma 8.2.2,

Otherwise S′ is a block specification S′ = Ty(BT) or Eq(\overline{p} : BT). By inversion, we have E ⊢ BT : blocktype and E ⊢ \overline{p} : BT in case of a manifest specification.

We will first show that E′ ⊢ BT : blocktype. By Lemma 5.8.7 we get the validity of judgments in Essence_{P} (E ⊢ BT : blocktype). Let us call J this set of judgments. Since by Lemma 5.8.1 every judgment in J is a term judgment of the form E; E_{3} ⊢ J′ where E_{3} is a term environment, we can apply Lemma 8.2.2 to get E′; E_{3} ⊢ ok and Lemma 8.2.1 to get E′; E_{3} ⊢ J

Let us call the resulting set of judgments J′. Of course,

J′ = {E′; E_{3} ⊢ J | E′; E_{3} ⊢_{P} J ∈ Essence_{P} (E′ ⊢ BT : blocktype)}

Since the function Essence_{P} (·) for block correctness judgments is constructed as an inversion of the formation rule, we can use judgments in J′ and the closure property (C5) to get E′ ⊢ BT : blocktype.

If S′ is a manifest block specification Eq(\overline{p} : BT) then also have to show E′ ⊢ \overline{p} : BT. We have to consider the cases of BT = RewSpec(E^{Γ}, R) and IndSpec(E^{I} := E^{C}) separately.

In the first case, from E ⊢ \overline{p} : RewSpec(E^{Γ}, R) we can deduce the validity of all ⊢ judgments corresponding to ⊢_{P} judgments in Essence_{P} (E′ ⊢ \overline{p} : RewSpec(E^{Γ}, R)). Since E′ ⊢ RewSpec(E^{Γ}, R) : blocktype we can use the rule (RewSpec/Sat) to get E′ ⊢ \overline{p} : RewSpec(E^{Γ}, R) itself.

In case BT = IndSpec(E^{I} := E^{C}) we have to use Lemma 8.1.5 applied to E ⊢ \overline{p} : IndSpec(E^{I} := E^{C}) in order to get

E ⊢_{P} \overline{p} : IndSpec(E^{I}′ := E^{C}′) and E ⊢ IndSpec(E^{I} := E^{C}) ≈ IndSpec(E^{I}′ := E^{C}′)

From the first judgment we get E′ ⊢ \overline{p} : IndSpec(E^{I}′ := E^{C}′) by Lemma 8.2.1 and by Lemma 4.1.15 we also get E′ ⊢ IndSpec(E^{I}′ := E^{C}′) : blocktype.

From the convertibility judgment, we can get the validity of judgments in

Essence_{P} (E ⊢ IndSpec(E^{I} := E^{C}) ≈ IndSpec(E^{I}′ := E^{C}′))

by Lemma 5.8.7 and hence the ⊢ judgments corresponding to ⊢_{P} judgments from

Essence_{P} (E′ ⊢ IndSpec(E^{I} := E^{C}) ≈ IndSpec(E^{I}′ := E^{C}′))

by Lemmas 8.2.2 and 8.2.1. Using E′ ⊢ IndSpec(E^{I}δ := E^{C}δ) : blocktype and E′ ⊢ IndSpec(E^{I}′ := E^{C}′) : blocktype and the latter set of judgment we can get

E′ ⊢ IndSpec(E^{I} := E^{C}) ≈ IndSpec(E^{I}′ := E^{C}′)

by (Conv/IndSpec).

Finally, using (Block/Conv) we get E′ ⊢ \overline{p} : IndSpec(E^{I} := E^{C}).

In both cases, for BT = IndSpec(E^{I} := E^{C}) and for BT = RewSpec(E^{Γ}, R), we get E′ ⊢ \overline{p} : BT and E′ ⊢ BT : blocktype, so we can apply (Eq/Form) to get E′ ⊢ Eq(\overline{p} : BT) : spec and (Env/Insert) to get E′; \overline{v} : Eq(\overline{p} : BT) ⊢ ok.

8.3 Removing modules

The first two lemmas of this section close the gap between Lemma 8.2.3 and Lemmas 7.1.2 and 7.1.4. The remaining three show that module removal operations: signature flattening, functor removal and elimination of manifest modules are bidirectional, i.e. a term judgment is valid before the operation if and only if it is valid after the operation.

Lemma 8.3.1. Let E_{1}; v : Ty(M); E_{2} ⊢ ok where E_{2} is an environment without modules and M = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End. Then E_{1}; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}; E_{2}δ ⊢ ok, where δ = \{\overline{v.v_{i}} ↦ \overline{v}_{i}\}.

Proof. By induction on the length of E_{2}. If E_{2} is empty then the result follows from the correctness of the signature M. Otherwise E_{1}; v : Ty(M); E_{2}; \overline{v}′ : S′ ⊢ ok implies E_{1}; v : Ty(M); E_{2} ⊢ ok and E_{1}; v : Ty(M); E_{2} ⊢ S′ : spec. Let E = E_{1}; v : Ty(M); E_{2} and E′ = E_{1}; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}; E_{2}δ. By induction hypothesis we have E′ ⊢ ok. Since S′ is not a module specification, it is either a term specification or a block specification.

If S′ is a term specification we just use Lemma 7.1.1.

If S′ is an abstract block judgment, we proceed in the same way as in the proof of Lemma 7.1.2. If S′ is a manifest block specification Eq(\overline{p} : BT) then by inversion we have E ⊢ \overline{p} : BT and E ⊢ BT : blocktype. From the second judgment, we can deduce E′ ⊢ BT δ : blocktype, just as in the proof of Lemma 7.1.2.

Now we have to consider the cases of BT = RewSpec(E^{Γ}, R) and IndSpec(E^{I} := E^{C}) separately.

In the first case, from E ⊢ \overline{p} : RewSpec(E^{Γ}, R) we can deduce the validity of all judgments in Essence_{P} (E′ ⊢ \overline{p}δ : RewSpec(E^{Γ}δ, Rδ)). Since E′ ⊢ RewSpec(E^{Γ}δ, Rδ) : blocktype we can use the rule (RewSpec/Sat) to get E′ ⊢ \overline{p}δ : RewSpec(E^{Γ}δ, Rδ) itself.

In case BT = IndSpec(E^{I} := E^{C}) we have to use Lemma 8.1.5 applied to E ⊢ \overline{p} : IndSpec(E^{I} := E^{C}) in order to get

E ⊢_{P} \overline{p} : IndSpec(E^{I}′ := E^{C}′) and E ⊢ IndSpec(E^{I} := E^{C}) ≈ IndSpec(E^{I}′ := E^{C}′)

From the first judgment we get E′ ⊢_{P} \overline{p}δ : IndSpec(E^{I}′δ := E^{C}′δ) by Lemma 5.9.1 and by Lemma 4.1.15 we also get E′ ⊢ IndSpec(E^{I}′δ := E^{C}′δ) : blocktype.

From the convertibility judgment, we can get the validity of judgments in

Essence_{P} (E ⊢ IndSpec(E^{I} := E^{C}) ≈ IndSpec(E^{I}′ := E^{C}′))

by Lemma 5.8.7 and hence in

Essence_{P} (E′ ⊢ IndSpec(E^{I}δ := E^{C}δ) ≈ IndSpec(E^{I}′δ := E^{C}′δ))

by Lemmas 7.1.1 and 5.9.1. Using E′ ⊢ IndSpec(E^{I}δ := E^{C}δ) : blocktype and E′ ⊢ IndSpec(E^{I}′δ := E^{C}′δ) : blocktype and the latter set of judgment we can get

E′ ⊢ IndSpec(E^{I}δ := E^{C}δ) ≈ IndSpec(E^{I}′δ := E^{C}′δ)

by (Conv/IndSpec).

Finally, using (Block/Conv) we get E′ ⊢ \overline{p}δ : IndSpec(E^{I}δ := E^{C}δ).

In both cases, for BT = IndSpec(E^{I} := E^{C}) and for BT = RewSpec(E^{Γ}, R), we get E′ ⊢ \overline{p}δ : BT δ and E′ ⊢ BT δ : blocktype, so we can apply (Eq/Form) to get E′ ⊢ Eq(\overline{p}δ : BT δ) : spec and (Env/Insert) to get E′; \overline{v} : Eq(\overline{p}δ : BT δ) ⊢ ok.

Lemma 8.3.2. Let E_{1}; v : Ty(M); E_{2} ⊢ ok where E_{2} is an environment without modules and M = Funsig(v̂ : M_{1})M_{2}. Then E_{1}; E_{2} ⊢ ok.

Proof. The proof is similar to the proof of Lemma 8.3.1.

Lemma 8.3.3. Let E = E_{1}; v : Ty(M); E_{2}, where E_{2} is an environment without modules and M = Sig \overline{v}_{1}▷\overline{v}_{1} : S_{1} … \overline{v}_{n}▷\overline{v}_{n} : S_{n} End. If E ⊢ ok then for every term judgment J,

E ⊢ J if and only if E′ ⊢ Jδ

where E′ = E_{1}; \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}; E_{2}δ and δ = \{\overline{v.v_{i}} ↦ \overline{v}_{i}\}.

Proof. The proof of the “only if” part is almost already done. If E ⊢ J then E ⊢_{P} J by Lemma 5.8.7 and since E′ ⊢ ok by Lemma 8.3.1, we can apply Lemma 5.9.1 to get E′ ⊢ Jδ.

The proof of the “if” part is also quite simple. If E′ ⊢ Jδ then by Lemma 4.1.4,

E_{1}; v : Ty(M); \overline{v}_{1} : S_{1} … \overline{v}_{n} : S_{n}; E_{2}δ ⊢ Jδ

Now, since E_{1}; v : Ty(M) ⊢ \overline{v.v_{1}} : S_{1}, by Lemma 4.1.5

E_{1}; v : Ty(M); \overline{v}_{2} : S_{2}\{\overline{v}_{1} ↦ \overline{v.v_{1}}\} … \overline{v}_{n} : S_{n}\{\overline{v}_{1} ↦ \overline{v.v_{1}}\}; E_{2}δ\{\overline{v}_{1} ↦ \overline{v.v_{1}}\} ⊢ Jδ\{\overline{v}_{1} ↦ \overline{v.v_{1}}\}

Now we have E_{1}; v : Ty(M) ⊢ \overline{v.v_{2}} : S_{2}\{\overline{v}_{1} ↦ \overline{v.v_{1}}\} and we can continue substituting \overline{v.v_{j}} for \overline{v}_{j} until j = n. In the end, we obtain

E_{1}; v : Ty(M); E_{2}δ\{\overline{v}_{i} ↦ \overline{v.v_{i}}\} ⊢ Jδ\{\overline{v}_{i} ↦ \overline{v.v_{i}}\}

Since δ\{\overline{v}_{i} ↦ \overline{v.v_{i}}\} is the identity, the latter is equivalent E ⊢ J, which concludes the proof.

Lemma 8.3.4. Let E = E_{1}; v : Ty(M); E_{2}, where E_{2} is an environment without modules and M = Funsig(v̂ : M_{1})M_{2}. If E ⊢ ok then for every term judgment J,

E ⊢ J if and only if E′ ⊢ J

where E′ = E_{1}; E_{2}.

Proof. The proof of the “only if” part is the following: E ⊢ J implies E ⊢_{P} J and since E′ ⊢ ok by Lemma 8.3.2, we have E′ ⊢ J by Lemma 5.9.2.

The proof of the “if” part simply follows by Lemma 4.1.4.

Lemma 8.3.5. Let E = E_{1}; v : Eq(p : M); E_{2}, where E_{2} is an environment without modules. If E ⊢ ok then for every term judgment J,

E ⊢ J if and only if E′ ⊢ J

where E′ = E_{1}; v : Ty(M/p); E_{2}.

Proof. The proof of the “only if” part is the following: E ⊢ J implies E ⊢_{P} J and since E′ ⊢ ok by Lemma 8.2.3, we have E′ ⊢ J by Lemma 8.2.1.

The proof of the “if” part is a bit more involved. If E′ ⊢ J then by Lemma 4.1.4,

E_{1}; v′ : Eq(p : M); v : Ty(M/p); E_{2} ⊢ J

Since E_{1}; v′ : Eq(p : M) ⊢ v′ : M and E_{1}; v′ : Eq(p : M) ⊢ p : M and E_{1}; v′ : Eq(p : M) ⊢ v′ ≈ p, we get E_{1}; v′ : Eq(p : M) ⊢ M/v′ <: M/p by Lemma 5.6.1.

Hence E_{1}; v′ : Eq(p : M) ⊢ v′ : M/p and consequently E_{1}; v′ : Eq(p : M) ⊢ v′ : Ty(M/p). By Lemma 4.1.5 we get

E_{1}; v′ : Eq(p : M); E_{2}{v ↦ v′} ⊢ J{v ↦ v′}

which is α-convertible with the desired conclusion E_{1}; v : Eq(p : M); E_{2} ⊢ J.

8.4 Removing manifest blocks

In this section we prove that the operation of removing manifest blocks is bidirectional (Lemma 8.4.5), i.e. given an environment E = E_{1}; \overline{v} : Eq(\overline{v}′ : BT); E_{2} without modules and E′ = E_{1}; E_{2}δ where δ = \{\overline{v} ↦ \overline{v}′\}, we prove that E ⊢ e : t if and only if E′ ⊢ eδ : tδ. The proof of the “only if” part is trivial, but proving the “if” part is more complicated. First, we need Lemma 8.4.4 to show that E′ ⊢ eδ : tδ implies E ⊢ e : tδ and then Lemma 8.4.2 to get E ⊢ t ≈ tδ and hence E ⊢ e : t by conversion.

Lemma 8.4.4 is quite complex and it needs a number of auxiliary facts. We start by proving that the environment with a manifest block subsumes the environment without the manifest block.

Lemma 8.4.1. Let E_{1}; \overline{v} : Eq(\overline{ṽ} : BT); E_{2} ⊢ ok where E_{1} and E_{2} are environments without modules and δ = \{\overline{v} ↦ \overline{ṽ}\}. If E_{1}; E_{2}δ ⊢ J then E_{1}; \overline{v} : Eq(\overline{ṽ} : BT); E_{2} ⊢ J.

Proof. We prove by induction on the length of E_{2} that the implication holds for all judgments J. If E_{2} is empty then it is trivial by Lemma 4.1.4. Otherwise E_{1}; \overline{v} : Eq(\overline{ṽ} : BT); E_{2}; \overline{v}′ : S′ ⊢ ok implies E_{1}; \overline{v} : Eq(\overline{ṽ} : BT); E_{2} ⊢ ok and E_{1}; \overline{v} : Eq(\overline{ṽ} : BT); E_{2} ⊢ S′ : spec. Let E = E_{1}; \overline{v} : Eq(\overline{ṽ} : BT); E_{2} and E′ = E_{1}; E_{2}δ.

By induction hypothesis if E′ ⊢ J then E ⊢ J for any judgment J.

Since E′; \overline{v}′ : S′δ ⊢ ok we have E′ ⊢ S′δ : spec. By induction hypothesis, we also have E ⊢ S′δ : spec. Since E ⊢ S′ : spec and E ⊢ \overline{v} ≈ \overline{ṽ}, by Lemma 4.1.13 we get E ⊢ S′ ≈ S′δ. By Lemma 4.1.10 we get E; \overline{v} : S′ ⊢ \overline{v} : S′δ. Consequently, E; \overline{v} : S′ ⊢ \overline{v}′′ : S′′ for every \overline{v}′′ : S′′ in E′; \overline{v} : S′δ and hence by Lemma 4.1.6 we get E; \overline{v} : S′ ⊢ J for every J such that E′; \overline{v} : S′δ ⊢ J.

Lemma 8.4.2. Let E = E_{1}; \overline{v} : Eq(\overline{v}′ : BT); E_{2} be an environment without modules and let δ = \{\overline{v} ↦ \overline{v}′\}. If E ⊢ ϕ : Ψ then E ⊢ ϕ ≈ ϕδ.

Proof. If E ⊢ ϕ : Ψ then E′ ⊢ ϕδ : Ψδ where E′ = E_{1}; E_{2}δ. By Lemma 8.4.1 E ⊢ ϕδ : Ψδ and since E ⊢ \overline{v} ≈ \overline{v}′, we have E ⊢ ϕ ≈ ϕδ by Lemma 4.1.13.

Lemma 8.4.4, the most complicated lemma of this section, requires a long proof by induction on the derivation in ⊢_{P}, using the function Essence_{P} (·) defined in Section 5.8 and the notion of direct type defined below.

Definition 8.4.1. Given an environment without modules E and a variable v, the direct type of v in E, denoted by E(v) is defined as follows:

• if E = E_{1}; v : S; E_{2} where S = Ty(t) or Eq(e : t) then E(v) = t

• if E = E_{1}; \overline{v} : S; E_{2} where \overline{v} = v^{1} … v^{n} and v = v^{i}, S = Ty(BT) or Eq(\overline{ṽ} : BT), then:

– if BT = RewSpec(v′_{1} : Ty(t_{1}) … v′_{n} : Ty(t_{n}), R) then E(v) = t_{i}

– if BT = IndSpec(v_{1}^{I} : Ty(t_{1}) … v_{k}^{I} : Ty(t_{k}) := E^{C}) and i ≤ k then E(v) = t_{i}

– if BT = IndSpec(v_{1}^{I} : Ty(t_{1}) … v_{k}^{I} : Ty(t_{k}) := v_{1}^{C} : Ty(t̂_{1}) … v_{l}^{C} : Ty(t̂_{l})) and i > k then E(v) = t̂_{i−k}{v_{j}^{I} ↦ v^{j}}

Lemma 8.4.3. Let E be an environment without modules. For every v in the domain of E, E ⊢_{P} v : E(v). Moreover if E ⊢ v : t then E ⊢_{P} t ≈ E(v).

Proof. The judgment E ⊢_{P} v : E(v) can be derived using the rule (P/Env/Lookup), followed by either (P/Ty/Type) or (P/Eq/Type), followed by (P/RewSpec/Fun) or (P/IndSpec/IndType) or (P/IndSpec/Constr) in case v is a part of a block.

The second part follows from the fact that every term judgment can be derived in ⊢_{P} and that the above derivation of E ⊢_{P} v : E(v) is the only one in ⊢_{P} which makes v appear on the left-hand side of : in the judgment E ⊢_{P} v : t. The only rule that does not change that is (P/Term/Conv) and therefore E ⊢_{P} t ≈ E(v) by transitivity.

Lemma 8.4.4. Let E = E_{1}; \overline{v} : Eq(\overline{v}′ : BT); E_{2} be an environment without modules, and let E′ = E_{1}; E_{2}δ where δ = \{\overline{v} ↦ \overline{v}′\}. If E ⊢ ok and E′ ⊢_{P} ϕδ : Ψ then all judgments in Essence_{P} (E ⊢ ϕ : Ψ) are satisfied.

Proof. We start by proving that E ⊢ v^{j} : E(v^{j}′) for every v^{j} ∈ \overline{v}. We have to consider two cases.

• If BT = RewSpec(E^{Γ}, R) with E^{Γ} = v_{1}^{Γ} : Ty(t_{1}) … v_{n}^{Γ} : Ty(t_{n}) or BT = IndSpec(E^{I} := E^{C}) with E^{I} = v_{1}^{I} : Ty(t_{1}) … v_{k}^{I} : Ty(t_{k}) and j ≤ k, then E_{1} ⊢ v^{j}′ : t_{j} and by Lemma 8.4.3 and Lemma 5.7.2, E ⊢_{P} t_{j} ≈ E(v^{j}′).

For v^{j} we have E(v^{j}) = t_{j} and hence E ⊢ v^{j} : t_{j}. Using (Term/Conv), we obtain E ⊢ v^{j} : E(v^{j}′).

• Otherwise BT = IndSpec(E^{I} := E^{C}) with E^{I} = v_{1}^{I} : Ty(t_{1}) … v_{k}^{I} : Ty(t_{k}) and E^{C} = v_{1}^{C} : Ty(t̂_{1}) … v_{l}^{C} : Ty(t̂_{l}) and j > k. In that case E_{1} ⊢ v^{j}′ : t̂_{j−k}{v_{i}^{I} ↦ v^{i}′} and E ⊢_{P} t̂_{j−k}{v_{i}^{I} ↦ v^{i}′} ≈ E(v^{j}′).

For v^{j} we have E(v^{j}) = t̂_{j−k}{v_{i}^{I} ↦ v^{i}} but fortunately E ⊢ v^{i} ≈ v^{i}′ for all i and we can use Lemma 4.1.13 to get E ⊢ t̂_{j−k}{v_{i}^{I} ↦ v^{i}′} ≈ t̂_{j−k}{v_{i}^{I} ↦ v^{i}}. By transitivity and conversion, we obtain E ⊢ v^{j} : E(v^{j}′).

Now we can do the proof by induction on the derivation of E′ ⊢_{P} ϕδ : Ψ. The most difficult is the rule (P/Env/Lookup). The conclusion of this rule is E′ ⊢_{P} \overline{ṽ}δ : S.

If \overline{ṽ}δ = \overline{ṽ} then by Lemma 8.4.1, E ⊢ \overline{ṽ} : S. Otherwise \overline{ṽ} ∩ \overline{v} ≠ ∅ and hence \overline{ṽ}δ ∩ \overline{v}′ ≠ ∅. In other words \overline{ṽ}δ : S is a part of E_{1}. We

consider the following cases, depending on S.

• If S = Ty(t), then \overline{ṽ}δ has length 1 and \overline{ṽ}δ = v^{j}′ for some j and consequently \overline{ṽ} = v^{j}. Since t = E(v^{j}′), we already know that E ⊢ v^{j} : t.

• If S = Eq(e : t), then \overline{ṽ} = v^{j} for some j as in the previous case and also E ⊢ v^{j} : t. In order to prove E ⊢ v^{j} ≈ e it is enough to notice that E ⊢ v^{j} ≈ v^{j}′ and E ⊢ v^{j}′ ≈ e.

• If S = Ty(RewSpec(E^{Γ}, R)) where E^{Γ} = v_{1}^{Γ} : Ty(t_{1}) … v_{n}^{Γ} : Ty(t_{n}) and the set of rules R = \{E_{i} | e_{i}^{L} ⟶ e_{i}^{R} : t̂_{i}\}_{i=1...m} then \overline{ṽ} has length n. We have

Essence_{P} (E ⊢ \overline{ṽ} : S) =
{E ⊢_{P} ṽ^{j} : t_{j} for j = 1 … n} ∪
{E; E_{i} ⊢_{P} e_{i}^{L}{v_{j}^{Γ} ↦ ṽ^{j}} ≈ e_{i}^{R}{v_{j}^{Γ} ↦ ṽ^{j}} for i = 1 … m}

By assumption we have E′ ⊢ ṽ^{j}δ : t_{j} for j = 1 … n and

E′; E_{i} ⊢ e_{i}^{L}{v_{j}^{Γ} ↦ ṽ^{j}δ} ≈ e_{i}^{R}{v_{j}^{Γ} ↦ ṽ^{j}δ} for i = 1 … m

If, for a given j ṽ^{j}δ = ṽ^{j} then by Lemma 8.4.1, we have E ⊢ ṽ^{j} : t_{j}. Otherwise, if ṽ^{j} = v^{i} and ṽ^{j}δ = v^{i}′ then we have E ⊢ v^{i} : t_{j}, because t_{j} = E(v^{i}′).

For convertibility judgments, it is enough to notice that by inverting the judgment E′ ⊢ RewSpec(E^{Γ}, R) : blocktype, substitution and Lemma 8.4.1, we get

E; E_{i} ⊢ e_{i}^{L}{v_{j}^{Γ} ↦ ṽ^{j}δ} : t̂_{i}{v_{j}^{Γ} ↦ ṽ^{j}δ} and E; E_{i} ⊢ e_{i}^{R}{v_{j}^{Γ} ↦ ṽ^{j}δ} : t̂_{i}{v_{j}^{Γ} ↦ ṽ^{j}δ}

Similarly, by inverting E ⊢ RewSpec(E^{Γ}, R) : blocktype and substitution we get

E; E_{i} ⊢ e_{i}^{L}{v_{j}^{Γ} ↦ ṽ^{j}} : t̂_{i}{v_{j}^{Γ} ↦ ṽ^{j}} and E; E_{i} ⊢ e_{i}^{R}{v_{j}^{Γ} ↦ ṽ^{j}} : t̂_{i}{v_{j}^{Γ} ↦ ṽ^{j}}

Since E ⊢ ṽ^{j} ≈ ṽ^{j}δ, we can apply Lemma 4.1.13 to get

E; E_{i} ⊢ e_{i}^{L}{v_{j}^{Γ} ↦ ṽ^{j}δ} ≈ e_{i}^{L}{v_{j}^{Γ} ↦ ṽ^{j}} and E; E_{i} ⊢ e_{i}^{R}{v_{j}^{Γ} ↦ ṽ^{j}δ} ≈ e_{i}^{R}{v_{j}^{Γ} ↦ ṽ^{j}}

This, by Lemma 8.4.1 and transitivity gives

E; E_{i} ⊢ e_{i}^{L}{v_{j}^{Γ} ↦ ṽ^{j}} ≈ e_{i}^{R}{v_{j}^{Γ} ↦ ṽ^{j}}

• If S = Ty(IndSpec(E^{I} := E^{C})) where E^{I} = v_{1}^{I} : Ty(t_{1}) … v_{k}^{I} : Ty(t_{k}) and E^{C} = v_{1}^{C} : Ty(t̂_{1}) … v_{n}^{C} : Ty(t̂_{n}) then the length(\overline{ṽ}) = k + l we have to show the validity of judgments in

Essence_{P} (E ⊢ \overline{ṽ} : S) =

{E ⊢_{P} ṽ^{j} : t_{j} for j = 1 … k} ∪ {E ⊢_{P} ṽ^{k+i} : t̂_{i}{v_{j}^{I} ↦ ṽ^{j}} for i = 1 … n}

The first set of judgments can be obtained in the same way as in the previous case. For the second set, by hypothesis we have E ⊢ ṽ^{k+i}δ : t̂_{i}{v_{j}^{I} ↦ ṽ^{j}δ}. Just as in the previous case it can be shown that

E ⊢ t̂_{i}{v_{j}^{I} ↦ ṽ^{j}δ} ≈ t̂_{i}{v_{j}^{I} ↦ ṽ^{j}}

using Lemma 4.1.13 since both substitutions are convertible and both types are correct.

Now if ṽ^{k+i}δ = ṽ^{k+i} then by conversion E ⊢ ṽ^{k+i} : t̂_{i}{v_{j}^{I} ↦ ṽ^{j}}.

Otherwise if ṽ^{k+i} = ṽ^{l} and ṽ^{k+i}δ = ṽ^{l}′ then E ⊢ ṽ^{l} : t̂_{i}{v_{j}^{I} ↦ ṽ^{j}δ}, because we have t̂_{i}{v_{j}^{I} ↦ ṽ^{j}δ} = E(ṽ^{l}′) and by conversion we get E ⊢ ṽ^{l} : t̂_{i}{v_{j}^{I} ↦ ṽ^{j}}.

• If S = Eq(\overline{v̂} : BT), then

Essence_{P} (E ⊢ \overline{ṽ} : S) = \{E ⊢ ṽ^{j} ≈ v̂^{j} for all j\} ∪ Essence_{P} (E ⊢ \overline{ṽ} : BT)

The first part can be shown as in the case for manifest term specifications and the second part is equal Essence_{P} (E ⊢ \overline{ṽ} : Ty(BT)) and can by shown like in one of the two previous cases.

So we are done with the rule (P/Env/Lookup). Other rules are much simpler to handle.

The rule (P/Sig/Access) is impossible in the environment without modules. The rules for specifications all follow by induction hypothesis since Essence_{P} (·) applied

to the conclusion is included in Essence_{P} (·) of the premises.

The term rules have to be treated with care. The rule (P/Term/Prod) in our case is the following.

\frac{E′ ⊢_{P} t_{1}δ : s_{1} E′; v : Ty(t_{1}δ) ⊢_{P} t_{2}δ : s_{2}}{E′ ⊢_{P} (v:t_{1}δ)t_{2}δ : s_{3}} if (s_{1}, s_{2}, s_{3}) ∈ R

By the first induction hypothesis, we get E ⊢_{P} t_{1} : s_{1}, and since E; v : Ty(t_{1}) ⊢ ok, by the second induction hypothesis, we get E; v : Ty(t_{1}) ⊢_{P} t_{2} : s_{2}. Hence we can apply (P/Term/Prod) and get E ⊢_{P} (v:t_{1})t_{2} : s_{3} as desired.

The rule (P/Term/Abs) is the following:

\frac{E′; v : Ty(t_{1}δ) ⊢_{P} eδ : t_{2} E′ ⊢_{P} (v:t_{1}δ)t_{2} : s}{E′ ⊢_{P} [v:t_{1}δ]eδ : (v:t_{1}δ)t_{2}}

By Lemma 5.7.3, the second premise must contain the subderivation for product with the premises E′ ⊢_{P} t_{1}δ : s_{1} and E′; v : Ty(t_{1}δ) ⊢_{P} t_{2} : s_{2} and the conclusion E′ ⊢_{P} (v:t_{1}δ)t_{2} : s_{3} where E′ ⊢ s_{3} ≈ s or s_{3} = s. So by induction hypothesis we get E ⊢ t_{1} : s_{1} and hence E; v : Ty(t_{1}) ⊢ ok. Using Lemma 8.4.1 we get E ⊢ t_{1}δ : s_{1} and E; v : Ty(t_{1}) ⊢ t_{2} : s_{2} and by (Term/Prod) we get E ⊢ (v : t_{1})t_{2} : s_{3}.

Using Lemma 8.4.2, we get E ⊢ t_{1} ≈ t_{1}δ and consequently E; v : Ty(t_{1}δ) ⊢ t_{2} : s_{2} and E ⊢ (v : t_{1}δ)t_{2} : s_{3} by (Term/Prod). Using (Conv/Prod) we can show that E ⊢ (v : t_{1})t_{2} ≈ (v : t_{1}δ)t_{2}.

Now we can use induction hypothesis applied to the first premise to get E; v : Ty(t_{1}) ⊢ e : t_{2} and apply (Term/Abs) to get E ⊢ [v:t_{1}]e : (v:t_{1})t_{2}. By conversion, we get E ⊢ [v:t_{1}]e : (v:t_{1}δ)t_{2} as desired.

The rule (P/Term/Axiom) is obvious.

The rule (P/Term/App) looks as follows:

\frac{E′ ⊢_{P} eδ : (v:t_{1})t_{2} E′ ⊢_{P} e′δ : t_{1}}{E′ ⊢_{P} eδ e′δ : t_{2}\{v ↦ e′δ\}}

By induction hypothesis we get E ⊢ e : (v:t_{1})t_{2} and E ⊢ e′ : t_{1}, so we can apply (Term/App) and get E ⊢ e e′ : t_{2}{v ↦ e′}.

Fortunately, by Lemma 8.4.2, E ⊢ e′ ≈ e′δ and since both t_{2}{v ↦ e′} and t_{2}{v ↦ e′δ} are correct by Lemma 5.10.1, we get E ⊢ t_{2}{v ↦ e′} ≈ t_{2}{v ↦ e′δ} by Lemma 4.1.13.

Hence by conversion we get E ⊢ e e′ : t_{2}{v ↦ e′δ}.

The rule (P/Term/Conv) is the following:

\frac{E′ ⊢_{P} eδ : t E′ ⊢_{P} t′ : s E′ ⊢_{P} t ≈ t′}{E′ ⊢_{P} eδ : t′}

By the first induction hypothesis, we get E ⊢ e : t and by Lemma 8.4.1 we get E ⊢ t′ : s and E ⊢ t ≈ t′. We can therefore apply conversion to get E ⊢ e : t′ as desired.

The block rules simply follow by induction hypothesis.

Lemma 8.4.5. Let E = E_{1}; \overline{v} : Eq(\overline{v}′ : BT); E_{2} be an environment without modules, and let E ⊢ ok. Moreover let E′ = E_{1}; E_{2}δ where δ = \{\overline{v} ↦ \overline{v}′\}.

• If E′ ⊢ eδ : t′ then E ⊢ e : t′.

• E ⊢ e : t if and only if E′ ⊢ eδ : tδ.

• If E ⊢ e_{1} : t_{1} and E ⊢ e_{2} : t_{2} then E ⊢ e_{1} ≈ e_{2} iff E′ ⊢ e_{1}δ ≈ e_{2}δ.

Proof. The first implication follows directly from Lemma 8.4.4.

The second statement is true, because the “only if” part follows from Lemma 4.1.5 and the “if” part can be proved using the first implication and Lemmas 8.4.2 and 5.10.1. Indeed, in case t is a sort, we have t = tδ and we get the result immediately by the first implication. Otherwise, by Lemma 5.10.1 we get E′ ⊢ tδ : s and hence E ⊢ t : s by the first implication. Now we can use Lemma 8.4.2 and get E ⊢ tδ ≈ t. Using the first implication and the rule (Term/Conv) we obtain the desired conclusion E ⊢ e : t.

The “only if” part of the third statement follows again by Lemma 4.1.5 and the “if” part can be proved using Lemma 8.4.2 and transitivity.

8.5 Relative decidability

Given the partial results proved in earlier sections of this chapter, we can state and prove our relative decidability result. In the decidability theorem we make the additional assumption that the side-conditions for formation rules for inductive blocks and rewriting blocks are decidable.

The two lemmas show two steps of our reduction of typability: first we remove module elements from the environment, and second, we remove manifest blocks.

Lemma 8.5.1. Any environment E and a term judgment J can be transformed deterministically in polynomial time into an environment E′ without modules and a term judgment J′ such that

E ⊢ J if and only if E′ ⊢ J′

Proof. It is enough to apply Lemmas 8.3.3, 8.3.4 and 8.3.5 until there is no more module components in the environment.

This procedure takes time polynomial in the weight of E and J. Indeed every abstract module removal has at most quadratic cost: for functor removal it is obvious and for signature flattening, the size of the substitution is linear, and performing a substitution is a traversal of the environment and the judgment, giving at most quadratic time. Every module removal reduces the weight, so their total cost is limited by the weight of E to the power 3.

Transforming a manifest module specification into an abstract one also takes linear time, does not reduce weight, but does not increase it either. Since the module component becomes abstract, it gets removed immediately afterwards. Therefore this operation can be performed at most once for every specification, its total time is limited by the weight of E to the power 2.

The preceding lemma is very important. It proves the correctness of the term typing algorithm implemented in Coq (see Section 9.2.4).

Lemma 8.5.2. Any environment E without modules and a term judgment J can be transformed deterministically in polynomial time into a classic environment E′ and a term judgment J′ such that

E ⊢ J if and only if E′ ⊢ J′

Proof. It is enough to apply Lemma 8.4.5 until there is no more manifest blocks in the environment. Every manifest block removal takes time quadratic in the size of the environment plus the size of the judgment.

When all manifest blocks are removed from the environment, we arrive at a classic environment.

Therefore type-checking terms in the calculus with modules reduces in polynomial time to type-checking terms in the classic environments in the system ⊢_{P}. We can easily extend this result to all expressions of our language.

Theorem 2. Type-checking in ⊢ is decidable if and only if type-checking in ⊢_{P} in a classic environment is decidable.

Proof. Lemmas 8.5.1 and 8.5.2 above show that type-checking terms in ⊢ reduces to typechecking in ⊢_{P} in a classic environments.

Type-checking blocks reduces to type-checking terms as well. Indeed, by Lemma 8.1.5 a judgment of the form E ⊢ \overline{p} : IndSpec(E^{I} := E^{C}) implies that E ⊢_{P} \overline{p} : IndSpec(E^{I}′ := E^{C}′) and E ⊢ IndSpec(E^{I} := E^{C}) ≈ IndSpec(E^{I}′ := E^{C}′) for some E^{I}′ and E^{C}′. Finding suitable E^{I}′ and E^{C}′ is decidable, because type-checking blocks in ⊢_{P} is straightforward. The only applicable rules are (P/Env/Lookup), (P/Sig/Access), (Ty/Type) and (Eq/Type) and one just has to follow the structure of the paths \overline{p}. Once E^{I}′ and E^{C}′ found, the second judgment can only be proved using the rule (Conv/IndSpec), which has only term premises and block correctness judgments, which again follow from some term judgments.

For rewriting blocks, E ⊢ \overline{p} : RewSpec(E^{Γ}, R) can be proved from Essence_{P} (E ⊢ \overline{p} : RewSpec(E^{Γ}, R)) and the latter contains only term judgments.

Decidability of modules comes from the principal types property, which is expressed for paths by Lemma 5.6.5 and for other module expressions by Lemma 4.2.1. The usual typing algorithm [62] may be applied which computes the principal type for module expressions. Its recursive calls are on structurally smaller expressions and base case are terms or blocks, for which typing is decidable.

Implementation in Coq

The practical aspect of the present thesis is the implementation of the module system in the Coq proof assistant. Modules are implemented in Coq version 7.4, officially released in February 2003.

The formalism presented in previous chapters has not yet been completely implemented. First of all the current implementation does not cover rewriting. Second, the module subtyping on signatures containing inductive types is less flexible in the implementation than in the theory, because inductive types cannot subsume types specified as Parameter. See discussion about correcting this deficiency in Section 12.2.

On the other hand the implementation covers several useful facilities, such as naming of module types, the notation with and most of all, interactive modules.

The extended abstract of this chapter has been accepted for presentation at the 16th International Conference on Theorem Proving in Higher Order Logics (TPHOLs 2003) [19].

9.1 The architecture of Coq

Coq is entirely written in Objective Caml [76]. The full system has about 120 000 lines of code organized in almost 500 files. The source tree is divided into the following directories:

kernel — the type-checker of the calculus of inductive constructions,

library — the backtracking mechanism, management of generic tables and objects, theory files and directories, and interface to the global environment,

parsing — abstract syntax trees and management of standard and user defined parsing and pretty-printing rules,

interp — translation from syntax-trees to untyped terms,

pretyping — analysis of untyped terms (generation of implicit arguments, etc.),

proofs — management of an interactive proof, tactics — predefined tactics and the tactic language,

toplevel — interactive loop.

The kernel (about 10% of the code) is the only critical part of the system. Bugs in other parts of Coq cannot compromise the mathematical correctness of the theorem prover [42].

The implementation of modules follows closely this practice. Module type-checking has been added to the kernel, while the management of extra-logical module features is implemented outside, in the library directory.^{1} Implementing the latter part turned out to be more difficult than implementing the type-checker. To cope with higher-order modules, a whole new mechanism of object substitutions has been introduced.

9.2 Modules in the kernel

In the type-theory tradition, the kernel of a proof assistant is the type-checker of the calculus implemented in the system. Up to version 7.3 the Coq kernel implemented the type-checker for the calculus of inductive constructions. With version 7.4, it also implements the type-checker for the module language.

The interface between the kernel and the rest of the proof assistant is the abstract type safe environment representing correct environments, together with lookup functions and insert functions, which return an updated environment only if type-checking succeeds. The kernel is functional, i.e. no in-place modification is performed and the insert functions return the new environment or fail if type-checking is not successful.

Compared to the calculus presented in the previous chapters, the environment of the proof assistant contains more information than just names and specifications. In particular, in order to implement extraction,^{2} implementations (proofs) must be present in the environment whenever given by the user.

An insert function, such as add constant, add inductive etc. (abbreviated by add *), called with add * E v S P, where E is a correct environment, v the name of the new element, S its specification and P an optional implementation^{3}, implements the following typing rule:

(Add) \frac{E ⊢ ok E ⊢ S : spec E ⊢ P : S}{E; v : S := P ⊢ ok} if v ∉ E

It is important to note here that even though the environment sometimes contains implementations, they are never used for typing. Note also that when the implementation P is removed from the (Add) rule, we get the rule (Env/Insert) from our calculus.

Adding modules requires the function add module to be implemented, together with the module type-checker. Like previous add * functions, this can be seen as implementing the rule (Add), with P a module body, and S a module specification.

1Beware! There is a file called library in the directory library.

2In Coq it is possible to extract the executable content from a proof of validity of a program specification

3For the sake of clarity we ignore the fact that parts of the specification may be synthesized.

However, it would be difficult for the user to write the whole definition of a module at once, especially for large modules containing many definitions and proofs. Moreover, it would contradict the interactive character of Coq.

9.2.1 Interactive modules

What seems natural in an interactive theorem prover is interactive modules. The user should have a possibility to declare the beginning of a new module, input definitions, declare and interactively prove some lemmas and then close the module, making all the entered definitions and lemmas become the components of the newly defined module. This is exactly what is implemented in version 7.4 of Coq. The following Coq session (on the left) results in a module body m (on the right) assigned to the name A:

Coq < Module A.
Interactive Module A started
Coq <   Definition T:=nat.
T is defined
Coq <   Definition x:=O.
x is defined
Coq <   Definition y:bool.
1 subgoal
  ============================
   bool
Coq <     Exact true.
Subtree proved!
Coq <   Defined.
Exact true.
y is defined
Coq < End A.
Module A is defined

                                                        Struct
                                                          Definition T:Set=nat.
                                                          Definition x:nat=O.
                                                          Definition y:bool:=true.
                                                        End

After closing a module, its components can be accessed using the dot notation:

Coq < Print A.x.
A.x = (0)
     : nat

A naive way to implement the End A operation would be to recover the whole body of the structure outside the kernel, restore the environment to be the one before Module A and call add module with the reconstructed module body and environment.

Let us see what this would mean in terms of kernel operations. Let us call E the environment before the Module A command. First, during the interactive session, the definitions of T, x, and y would have to be checked by the kernel. After End A the non-critical part of code would reconstruct the module body m and the kernel function add module would be called with the environment E, the name A and the module body m. Like all add * functions, add module would type-check m before returning the new environment, according to the (Add) rule. Now let us concentrate on the right premise of this rule. In our case P is m and therefore the rule (Sig/Struct) must be used, all the premises of which have already been verified during the interactive phase of module construction.

The naive implementation of module ending would make the kernel do the same work twice. For this reason we decided to turn the beginning and ending of the module into kernel operations, which allowed to avoid redundancy.

So the interface of the kernel to the rest of the system has been extended with the functions start module, marking the beginning of a module, and end module, implementing the rule:^{4}

\frac{E; v_{1} : S_{1} := P_{1} … v_{n} : S_{n} := P_{n} ⊢ ok}{E; V : Ty( Sig v_{1} : S_{1} … v_{n} : S_{n} End ) := Struct v_{1} : S_{1} := P_{1} … v_{n} : S_{n} := P_{n} End ⊢ ok}

where V is the module name. The premise of this rule implies the correctness of the signature and the admissibility of this rule follows from the fact that the only possible derivation of the premise is by a sequence of (Add) rules. Their right premises imply

E ⊢ Struct v_{1} : S_{1} := P_{1} … v_{n} : S_{n} := P_{n} End : Sig v_{1} : S_{1} … v_{n} : S_{n} End

by an application of (Sig/Struct). Using (Add) applied to E ⊢ ok and the above, we get the desired conclusion.

The same interactive mechanism can also be used to declare functors. Given a signature SIG (on the right^{5}), one can define an interactive functor (on the left):

Coq < Module Two[X:SIG][Y:SIG].
Interactive Module Two started
Coq <   Definition T:=X.T * Y.T.
Coq <   Definition x:=(X.x, Y.x).
Coq < End Two.
Module Two is defined

                                                         SIG = Sig
                                                                 Parameter T:Set.
                                                                 Parameter x:T.
                                                               End

In this case the environment operation performed by the function end module shortcuts not only the (Sig/Struct) rule, but also (l applications of) the (Funsig/Functor) rule, leading to the admissible rule:

\frac{E; V_{1} : Ty(M_{1}) … V_{l} : Ty(M_{l}); v_{1} : S_{1} := P_{1} … v_{n} : S_{n} := P_{n} ⊢ ok}{E; V : Ty( Funsig(V_{1} : M_{1}) … Funsig(V_{l} : M_{l}) Sig v_{1} : S_{1} … v_{n} : S_{n} End ) := Functor[V_{1} : M_{1}] … Functor[V_{l} : M_{l}] Struct v_{1} : S_{1} := P_{1} … v_{n} : S_{n} := P_{n} End ⊢ ok}

4In this section and the next one we do not distinguish labels and binders in structures and signatures.

5By the convention from Section 1.2, Parameter v:e is understood as a signature component v : Ty(e).

where V_{1}, …, V_{l} are formal parameters of the functor, M_{1}, …, M_{l} are their module types, and the triples v_{i} : S_{i} := P_{i} are the functor body components.

An optional element of a module opening declaration is the expected module type. After the following definition:

Coq < Module A':SIG.
Coq <   Definition T:=bool.
Coq <   Definition x:=true.
Coq < End A'.

the signature of the module body is matched against the signature SIG and if the matching succeeds, the interface of A' is restricted to SIG, i.e. the specification given to this component is Ty(SIG). In our example it means that the conversion equalities A'.T≈bool and A'.x≈true are forgotten, making both these components abstract.

If a restricting signature is present, the implementation of the end module function also involves application of the (Subsumption) rule.

9.2.2 Overview of the environment structure

In order to implement the function end module the type representing correct environments has to contain the information about the number of definitions to be transformed into structure components, the number of module declarations to be turned into functor parameters and (optionally) against what signature the body must be matched.

Therefore the type safe environment represents in fact a stack of growing correct environments E_{1} ⊆ ⋯ ⊆ E_{n}. Thanks to functional data structures and sharing, the elements of the environment E_{1} are not copied in each E_{2}, … E_{n}.

The operations add * affect only the last environment, i.e. insertion in E_{1} ⊆ ⋯ ⊆ E_{n−1} ⊆ E_{n} results in the environment stack E_{1} ⊆ ⋯ ⊆ E_{n−1} ⊆ (E_{n}; v : S := P).

The operations which change the length of the environment stack are start module and end module. The first one is called with an environment stack E_{1} ⊆ ⋯ ⊆ E_{n}, a name for the new module V, its parameters V_{1} : M_{1} … V_{l} : M_{l} and optionally output signature M_{out}. The function verifies correctness of M_{1}, … M_{l} and M_{out} and the fact that names V, V_{1}, …, V_{l} are fresh and returns E_{1} ⊆ ⋯ ⊆ E_{n} ⊆ E_{n+1}, with

E_{n+1} = E_{n}; V_{1} : Ty(M_{1}) … V_{l} : Ty(M_{l}); struct⟨V, M_{out}⟩

where struct⟨V, M_{out}⟩ is a special mark that is transparent for typing rules. The mark is only used by end module as a placeholder separating functor arguments from structure components and keeping the optional output signature M_{out}.

The correctness of the new environment follows from the fact that E_{n+1} can be obtained by applying l times the rule (Add).

The function end module called with E_{1} ⊆ ⋯ ⊆ E_{n−1} ⊆ E_{n} ⊆ E_{n+1}, returns E_{1} ⊆ ⋯ ⊆ E_{n−1} ⊆ E′_{n} where E′_{n} = E_{n}; V : Ty(M) := m with M and m synthesized from the information contained in E_{n+1}. More precisely, if E_{n+1} is

E_{n}; V_{1} : Ty(M_{1}) … V_{l} : Ty(M_{l}); struct⟨V, M_{out}⟩; v_{1} : S_{1} := P_{1} … v_{k} : S_{k} := P_{k}

m and M become respectively

Functor[V_{1} : M_{1}] … Functor[V_{l} : M_{l}] Struct v_{1} : S_{1} := P_{1} … v_{k} : S_{k} := P_{k} End
and Funsig(V_{1} : M_{1}) … Funsig(V_{l} : M_{l}) M_{out}

after checking that E_{n}; V_{1} : Ty(M_{1}) … V_{l} : Ty(M_{l}) ⊢ Sig v_{1} : S_{1} … v_{k} : S_{k} End <: M_{out}. In case M_{out} is missing, M becomes

Funsig(V_{1} : M_{1}) … Funsig(V_{l} : M_{l}) Sig v_{1} : S_{1} … v_{k} : S_{k} End

A similar mechanism is also used to implement interactive module types:

Coq < Module Type SIG.
Interactive Module Type SIG started
Coq <   Parameter T:Set.
T is assumed
Coq <   Parameter x:T.
x is assumed
Coq < End SIG.
Module Type SIG is defined

The function start modtype, called with an environment stack E_{1} ⊆ ⋯ ⊆ E_{n}, a name for the module type V_{M} and the list of parameters V_{1} : M_{1} … V_{l} : M_{l} (nonempty, if one wants to define a functor type) verifies the correctness of module types M_{1}, … M_{l}, the freshness of names V_{1}, … V_{l} and V_{M} and returns E_{1} ⊆ ⋯ ⊆ E_{n} ⊆ E_{n+1}, where E_{n+1} = E_{n}; V_{1} : M_{1} … V_{l} : M_{l}; sig⟨V_{M}⟩ where sig⟨V_{M}⟩ is a mark as before.

The corresponding closing function, end modtype, called with an environment stack E_{1} ⊆ ⋯ ⊆ E_{n−1} ⊆ E_{n} ⊆ E_{n+1} returns the environment E_{1} ⊆ ⋯ ⊆ E_{n−1} ⊆ E′_{n} where E′_{n} = E_{n}; V_{M} ≡ M with M synthesized from E_{n+1}. More precisely, if E_{n+1} is

E_{n}; V_{1} : Ty(M_{1}) … V_{l} : Ty(M_{l}); sig⟨V_{M}⟩; v_{1} : S_{1} … v_{k} : S_{k}

M becomes

Funsig(V_{1} : M_{1}) … Funsig(V_{l} : M_{l}) Sig v_{1} : S_{1} … v_{k} : S_{k} End

Since E_{n+1} is correct, we get E_{n} ⊢ M : modtype by applying (Sig/Form), followed by l applications (Funsig/Form).

Assigning names to module types is not covered by the theoretical part of this thesis. Unfolding of named module types may be treated like a textual macro expansion. It is implemented in the kernel only for efficiency reasons, i.e. the well-formedness of the abbreviated module types is checked only once.

Non-interactive module and module type definitions are implemented through the functions add module and add modtype. Note however that by the time of writing these words, parsing of whole structures and signatures is not implemented. Therefore non-interactive module definitions are basically limited to functor applications

Coq < Module DoubleA:=Two A A.
Module DoubleA is defined

and module renamings

Coq < Module A':=A.
Module A' is defined

The first one corresponds to an abstract module component DoubleA : Ty(M) := Two A A where M is the module type inferred for the functor application using the (Mod/App) rule. The second definition corresponds to a manifest module component A' : Eq(A : M_{A}/A) := A, where M_{A} is the principal module type of A.

9.2.3 Avoiding name clashes

The possibility of having nested modules forces us to take care of name clashes. This problem has already been discussed in Section 1.5.2. Since the solutions we adopted are slightly different in the theoretical and practical parts of our work, we present the problem again. Consider the following nested module:

Module F.
  Definition T:=nat.
  Definition x:T:=O.
  Module N.
    Definition T:=bool.
    Definition y:=x.
    Definition z:T:=true.
  End N.
  Definition z:=N.y.
End F.

The problem here is the two occurrences of the name T in the scope of the inner module. The simplest solution of rejecting every piece of code having two equal short names would be too restrictive, because the names of module components are important and the user cannot change them arbitrarily.^{6} The second solution consisting in hiding completely the previous definition with the new one is also impossible, because the old definition may be accessed indirectly, as shown by our example (the type of y is the first T). Using the Coq tradition to internally represent names together with the section path (Top.F.T for the first T and Top.F.N.T for the second) does not work well with functors (if F was a functor) and module renamings, like Module G:=F.

The solution adopted in the theoretical part of this thesis follows the idea of Harper and Lillibridge [48], also adopted by Courant [32]. In this writing the primary signature of the module F would look as follows:

6This strict policy is applied for functor parameters, since once a functor is defined, its parameters names are no longer important.

Sig T ▷ T : Eq(nat : ⋆) x ▷ x : Eq(0 : T) N ▷ N : Ty( Sig T ▷ T′ : Eq(bool : ⋆) y ▷ y : Eq(x : T) z ▷ z : Eq(true : T′) End ) z ▷ z : Eq(N.y : T)

End

To calculate the specification of F.z one has to use the substitution {T ↦ F.T, x ↦ F.x, N ↦ F.N} on Eq(N.y : T), obtaining

F.z : Eq(F.N.y : F.T)

Leroy in [59] and in the implementation of Ocaml attaches a unique number to every component name. The name (character string) plays the role of label and the name with the unique number (called identifier) plays the role of binder:

Sig T_{1} : Eq(nat : ⋆) x_{2} : Eq(0 : T_{1}) N_{3} : Ty( Sig T_{4} : Eq(bool : ⋆) y_{5} : Eq(x_{2} : T_{1}) z_{6} : Eq(true : T_{4}) End ) z_{7} : Eq(N_{3}.y : T_{1})

End

To calculate the specification of F_{0}.z, one takes the substitution {T_{1} ↦ F_{0}.T, x_{2} ↦ F_{0}.x, N_{3} ↦ F_{0}.N} and applies it to the specification given by the module type, obtaining again

F_{0}.z : Eq(F_{0}.N.y : F_{0}.T)

The solution we adopted consists in assigning a unique number not to every component name, but only to the leading Sigkeyword. The signature of F is then represented internally as follows:

Sig 1

T : Eq(nat : ⋆) x : Eq(0 : 1 .T) N : Ty( Sig 2 T : Eq(bool : ⋆) y : Eq( 1 .x : 1 .T) z : Eq(true : 2 .T) End ) z : Eq( 1 .N.y : 1 .T)

End

Now, to calculate the specification of 0 .F.z one only has to use the short substitution { 1 ↦ 0 .F }, obtaining

0 .F.z : Eq( 0 .F.N.y : 0 .F.T)

In the above, 0 is a unique identifier attached to the outer block, as the unique identifiers are not only attached to every Sig, but to every Struct and marks struct⟨·⟩ and sig⟨·⟩ as well. Moreover we assume that the first element of the environment is the mark struct⟨·⟩ with the self identifier 0 . In other words the environment stack is never empty, and the unique identifier of the current structure or signature is stored in the corresponding mark.

Of course the user never sees these unique identifiers. The name table mechanism, described in Section 9.3.5, efficiently separates user names from internal representation. It is also interesting to note that our unique identifiers bear a lot of similarities to the self identifier used in object oriented programming.

9.2.4 Modules and sections

Before implementing modules, the section mechanism was the only available mechanism to group together related definitions inside one file. The main advantage of sections, however, is a mechanism of abstracting individual definitions. The following code

Coq < Section S.
Coq <   Variable A:Set.
Coq <   Definition x := [a:A]a.
Coq <   Definition y := x.
Coq < End S.
x is discharged.
y is discharged.

is equivalent to

Coq < Definition x := [A:Set; a:A]a.
x is defined
Coq < Definition y := [A:Set](x A).
y is defined

Of course parametric modules (functors) do not subsume the functionality of sections. The interactions between the two abstracting mechanisms are not straightforward. When a section is inside a module (or a module type), the components of the module (or the module type) generated by the section are exactly those which result from the closing of the section.

On the other hand the vision of modules inside a section is not very clear. Abstracting a whole module with respect to a variable is not covered by the theory. On the other hand abstracting individual components of a module raises the question of interaction of to-be-abstracted modules with the ones defined outside the section.

So, for the time being we resigned from implementing modules inside sections, leaving only sections inside modules.

9.2.5 The with notation

In order to make it easy to enter similar module types, the with notation has been implemented. For example if SIG denotes the signature

Sig
  Parameter a:Set
  Declare Module N:SIG'
End

the module type SIG with Definition a:=nat is equivalent to the signature

Sig
  Definition a:Set:=nat
  Declare Module N:SIG'
End

and the module type SIG with Module N:=M is equivalent to

Sig
  Parameter a:Set
  Declare Module N:=M
End

provided that the type of M is a subtype of SIG'.

Module signatures modified by with Module or with Definition are evaluated to plain signatures in the process of type-checking.

9.2.6 Implementation details

We discuss in detail all the new components added to the kernel in order to be able to type-check modules. We start by describing the elements of the most low-level kernel file, i.e. names.

Representation of names. Thanks to the simplifying assumption that every environment represents an open structure or an open signature, every name of term level object (constant, inductive etc.) can be represented as an element of some possibly open module. Moreover in order to account for the possibility of having open sections inside modules, the abstract type of names of objects is represented by triples:

type kernel_name = module_path * dir_path * label

where label is just a character string, dir path is a list of names of nested open sections, and module path is the following variant type

type module_path =
  | MPfile of dir_path
  | MPbound of mod_bound_id
  | MPself of mod_self_id
  | MPdot of module_path * label

representing a module read from a file, a bound module (i.e. functor parameter), an open module or a submodule respectively. We also assume that the dir path part of any

kernel name is empty unless the module path is MPself .

Substitutions. As it was introduced in Section 9.2.3, the substitutions are necessary to calculate the specification of a module component or to calculate the signature of a functor application result.

The abstract type substitution is represented by a dictionary from the type of unique identifiers (representation of abstract types mod bound id and mod self id) to module path, and equipped with the functions for creating empty and singleton substitutions and with functions extending substitutions.

In order to apply the substitution, for every substitutive type a suitable substitution function must be defined. The first substitutive type is of course module path, for which the function

val subst_mp : substitution → module_path → module_path

is defined.

Substitution functions for other substitutive types are in fact mappings of this one. This is the case for kernel name, whose substitution function is

val subst_kn : substitution → kernel_name → kernel_name

The last element regarding the substitutions in the file names is the function for sequentially composing substitutions:

val join : substitution → substitution → substitution

such that for all substituting functions subst *, we have

               subst * (join θ_{1} θ_{2}) t = subst * θ_{2} (subst * θ_{1} t)

Environment and safe environment. In previous version of Coq, the type of environment env was declared in the file environ, all typing functions operated on env and then the type of environment was reexported abstractly in the file safe typing as the type

safe environment.

In our implementation, we do the same thing, but instead of simply reexporting the type env, we implement in the file safe typing the environment with marks and functions

start module and end module.

The implementation of the basic environment env has not changed much. The only modifications are the removal of information regarding modules read from disk together with management functions import and export (moved to safe typing), and addition of dictionaries storing modules and module types.

The implementation of the type save environment, representing environment stacks (see Section 9.2.2), is a nested record structure, where each record represents one open module or module type and contains its name, other information like the list of parameters or the optional output signature and the list of components of current structure or signature.

Apart from these elements it contains the env environment representing the environment without marks that is used by all typing functions. For efficiency reasons, env contains precomputed specifications of all module components in substituted form.

To understand what actions are performed by start * and end * functions, consider the following code fragment.

Module Type S.
  Definition T:=nat.
  Declare Module M.
    Parameter a:T.
    Definition b:=a.
  End M.
End S.

The (simplified) form of the safe environment before and after End M is shown in Figure 9.1.

The functions start * add a new record to the list. The functions end * remove the most recent record and add one declaration to the component list and env of the previous record. If the ended element was a structure, information about its components is added to the env as well.

Modifications in term typing. As the module language and the term language are independent (the module language is on top of the base language), the existing code for type-checking terms was barely changed.

The change in the representation of names did not force any modifications of the algorithm itself. The kernel names are used in the term typing algorithm in an abstract way, just as a type with equality. This was the case in previous versions of Coq, where names were represented by the type section path, and this is the case now.

From the theoretical point of view the type kernel name corresponds to term access paths. The precomputation of substituted specifications for kernel names follows the principal derivations of the specifications for access paths using rules (P/Env/Lookup), (P/Sig/Access) and (P/Ty/Type), (P/Eq/Type) for module specifications. Apart from precomputing the specifications, the term typing algorithm does not use module typing at all. The correctness of this approach is proved in Lemma 8.5.1. Indeed the algorithm works as indicated by the proof of this lemma: precomputation corresponds to signature flattening (Lemma 8.3.3) and the treatment of manifest modules follows Lemma 8.3.5. Even though the modules stay in the environment, the term typing algorithm works as if they were removed.

A more profound change in the typing algorithm was due to the notion of equivalence of inductive definitions. In the following situation

Module M.
  Inductive T : Set := a : T | b : T.
  Definition f := [x:T] Cases x of a => true | b => false end.
End M.
Module N:=M.

old -none-

msid 0
sign [ ]
env empty

old

sig⟨S⟩

msid S1

sign

                          T:Eq(nat:Set) [ ]

env

             S1 .T ↦ Eq(nat:Set)

old

struct⟨M⟩

msid M2

sign

                                                         a:Ty( S1 .T)  b:Eq( M2 .a: S1 .T) [ ]

env

                                           S1 .T ↦ Eq(nat:Set)

M2 .a ↦ Ty( S1 .T)

                                           M2 .b ↦ Eq( M2 .a: S1 .T)

old -none-

msid 0
sign [ ]
env empty

old

sig⟨S⟩

msid S1

sign

                          T:Eq(nat:Set)  M:Ty(Sig M2 a:Ty( S1 .T) b:Eq( M2 .a: S1 .T) End) [ ]

env

              S1 .T ↦ Eq(nat:Set)
              S1 .M ↦ Ty(Sig M2 a:Ty( S1 .T) b:Eq( M2 .a: S1 .T) End)
              S1 .M.a ↦ Ty( S1 .T)
              S1 .M.b ↦ Eq( S1 .M.a: S1 .T)

Figure 9.1: The environment before (left) and after (right) End M. the term (M.f N.a) should be well-typed. To achieve this the inductive definition in the module N must be given a manifest specification, making N.T and M.T equal, together with their constructors.

The typing algorithm does not use the results of Section 8.4 about removing manifest block specifications. Instead, manifest inductive blocks are treated directly by the conversion algorithm.

This equivalence between inductive blocks is marked in a new field mind equiv in the record type representing inductive definitions, mutual inductive body, in the file declarations. The convertibility test was changed accordingly, i.e. a new function mind equiv was added in the file closure for testing the equivalence and the cases for inductive types and constructors were modified in the function eqappr in the file reduction.

Module typing. The topmost file of the module type-checking is mod typing. It implements functions

val translate_modtype : env → module_type_entry → module_type_body
val translate_module : env → module_entry → module_body

which type-check the information given by the user (* entry) and translate it to the form stored in the environment (* body) or report an error if the given information is incorrect. These functions use the typing of the calculus of inductive constructions (in file term typing), module subtyping and various auxiliary and error reporting functions from the file modops.

The type-checking algorithm is mostly standard. It closely follows the typing algorithm presented in [62] and implemented in Ocaml. The only difference is the careful treatment of the predicative universe hierarchy constraints.

Universe constraints. The current implementation of universe constraints is not completely satisfactory from modularity point of view. In particular the implementation of a module may introduce important universe constraints that are not reflected by the specification. Therefore the correctness of an application of a functor to a module is not guaranteed by the successful subtyping. One also has to verify that constraints generated by the functor and the module are not contradictory. Therefore modifications of a module which do not change the module interface do require retype-checking of all uses of the module, which contradicts modularity.

To guarantee the correctness of module type-checking with respect to universe constraints, we collect them at the module subtyping and store them in the structure representing translated module expressions.

All functions in the file subtyping take a set of constraints C as the first argument, some parameters needed for comparison (like the environment etc.) and then two elements e_{1} and e_{2} to compare. They return the set C augmented by constraints resulting from the comparison e_{1} <: e_{2}. In this way the constraints are accumulated throughout the exported

check subtypes function.

Subtyping is essentially performed at two occasions: while type-checking a functor application and while defining a module with the explicit expected module type.

In the first situation the subtyping constraints are stored in the third component of the MEBapply constructor, representing the application node in the type module expr body in the file declarations. The latter type represents type-checked module expressions.

The constraints resulting from the second situation, where subtyping is used, are stored in the field mod constraints of type module body in the file declarations. This type represents the verified module definition, i.e. its specification and implementation.

In the end the constraints are duly added to the environment by full add module implemented in safe typing. The constraints are those stored in the module expression, in the module type and resulting from subtyping. Functions add * constraints in the file mod typing are responsible for the traversal of the module expression and the module type in quest for the constraints stored in application nodes of the module expressions and components of structures and signatures.

File modules — libraries. Modules stored on disk are treated very much like all other modules. To avoid misunderstanding they are called libraries. The only difference outside the file safe typing is that unlike other modules, libraries are given the absolute module name MPfile , and are not elements of a higher level structure.

In previous version of Coq, the code dealing with libraries was situated in file environ. In the current version all the code is in file safe typing. In fact the code for loading libraries (in function import) is almost identical to adding any other module once we know it is type correct. The only difference about disk modules is that they have to be reloaded every time the open structure or signature where they were loaded is closed:

Module M.
  Definition x:=[a:nat]a.
  Module N.
    Read File.
    Definition y:=(x File.number).
  End N.
End M.

In the above code fragment, the body of File is entered in the environment three times: inside N, after closing N and after closing M. This is inefficient, but may easily be avoided by the user by reading all libraries before opening any interactive modules. The reloading operations are taken care of by the functions end module and end modtype, through suitable operations on the loads safe environment field.

Compilation of disk modules bears also many similarities with usual modules. In fact it is implemented almost exactly like an interactive module. We start compilation by calling the function start library, which is a simplified version of the function start module (currently a library cannot be a functor). When the compilation is finished, the data of the toplevel structure is gathered and returned as an abstract type compiled library through the function export.

To assure the integrity of libraries the list of dependent libraries together with their checksums is stored with every compiled module. Similarly when a library is loaded the checksums of its dependencies are verified. For the sake of efficiency, however, the kernel does not calculate the checksums itself but relies on the caller of the import function. Of course, sacrificing efficiency, it can be changed to make the kernel even more self-contained.

It is important to notice that treatment of libraries like other modules enables further evolution of libraries from simple structures to possibly parametric structures (functors). It is also possible (putting universes apart) to split libraries into interfaces and implementations, like it is the case in Ocaml. There is absolutely nothing on the kernel side that prevents any of the above evolutions. The only modifications would have to be done in file safe typing. Much more work would have to be done outside the kernel though.

Summary of kernel files. We will list here the more important changes in kernel files.

names — added definitions of types module path, kernel name and substitution; re-

     moved section path.

term — added the substitution function subst mps^{7} on the type constr.

declarations — added new types representing correctly typed elements:

module type body (module type expressions), module expr body (module expressions), module specification body (module specifications), module body (module definitions), etc.;

added substitution functions on types constant body and mutual inductive body;

const body made lazy for efficiency of library loading;

added field mind equiv for equivalence of inductive specifications.

environ — support for importing and exporting libraries moved to safe typing; added dictionaries of modules and module types and functions dealing with them.

closure — added function mind equiv for equivalence of inductive specifications.

reduction — modified two cases of eqappr to deal with equivalence of inductive specifications.

entries — new file: types representing non-type-checked inductive and constant defini-

     tions (mutual inductive entry and constant entry) moved from indtypes and

safe typing respectively;

added types representing non-type-checked module related elements: module type entry (for module type expressions), module expr (module expressions), module entry (definition/declaration of a module).

7There are quite a few substitution functions for constr, dealing with de Bruijn indices, the section mechanism etc., so the name subst constr would be too meaningless.

Figure 9.2: The old and the new structure of the kernel 170 modops — new file: contains functions for module and module type substitutions, for

recursively adding module components to the environment, strengthening, signaling module errors and other auxiliary functions.

subtyping — new file: contains a function calculating universe constraints resulting from subtyping or calling an error function if subtyping incorrect.

term typing — new file: contains functions translating constant and inductive entries to bodies and checking local assumptions and definitions (all of them moved from safe typing).

mod typing — new file: contains functions for translating module and module type entries to bodies and functions adding module and module type universe constraints to the environment.

safe typing — added new functionality of the safe environment: addition of modules and module types, interactive modules and module types, importing and exporting libraries (moved from environ); some of the type-checking code moved to term typing.

9.3 Modules outside the kernel

The most important challenge of the introduction of modules in Coq was not the implementation of the type-checking algorithm. Apart from managing the certified environment, Coq has extra-logical features like implicit arguments, an extensible pretty-printer and an extensible parser, databases for automated tactics, etc. They are controlled through commands which can very well be issued inside modules or module types. The management of these features in the presence of modules was technically much more difficult than the modifications in the kernel.

9.3.1 Overview

The generic mechanisms responsible for the management of extra-logical features of Coq are implemented in the directory library. The functionality of the files in this directory are the following:

1. Global environment

The file global contains the reference to a safe environment and reexports the interface of the kernel file safe typing, but after every operation, the value of the reference is set to the modified environment.

2. Backtracking and state management

Backtracking is implemented through the generic extensible mechanisms of library objects and summaries.

most recent state^{reset until this old top}  ^{to be recached new top}

Figure 9.3: Backtracking

3. Theory file management

Compiling theory files and loading them, management of dependencies and logical/physical paths. This is implemented in the file library.

4. Name visibility management

Every Coq element has a unique long name, like Coq.Datatypes.nat for the type of natural numbers. The validity of its suffixes is stored in the name table, implemented in the file nametab.

5. Declaration of constants, variables etc.

6. Features: implicit arguments, generic options, etc.

The module operations on extra-logical features are implemented in a new file declaremods. Of course many other files were also modified. Before going into details, let us briefly present the generic mechanisms of Coq.

Backtracking and library objects.

Since Coq is an interactive proof assistant, it is important to let users correct the mistakes they make. Therefore a backtracking mechanism is implemented in Coq (through the Reset command).

The commands issued by the user are pushed on a stack, and from time to time, the full state of the system is pushed there as well. To backtrack to a given point it is enough to “recall” the most recent copy of the state preceding the point and redo the commands following the state and preceding the point. See Figure 9.3 for a graphical presentation (copies of the state are marked gray, and regular commands – white).

In order to make this mechanism work properly and be easily extensible, the “commands on the stack” are given a common interface of so called library objects^{8} implemented in the file libobject. The object method cache function implements the non-functional part of command execution (usually table updates) and is called when pushing the object to the command stack (by the function add leaf), and while backtracking (by the function reset – both functions in the file lib.

8In fact for historical reasons, they are not Objective Caml objects.

In previous versions of Coq the only other uses of the generic object mechanism was to store and restore (to and from the disk) the extra-logical elements of compiled theory files (methods load function and open function) and to partially control the discharge at the end of a section (the method export function).

Summaries and state

An important aspect of the implementation of backtracking is the possibility to periodically store the state of the system. Various Coq features require mutable information to work properly, for example the Auto tactic uses hint databases, the pretty-printing mechanism uses tables of pretty-printing rules, parsing — of parsing rules etc. Appropriate commands change the contents of these tables, so the contents of each table is a part of the state, and therefore should be stored. Of course copying the contents of each table would waste a lot of memory, so the data-structures used in Coq are all functional and their “copying” is just a pointer copying.

In order to facilitate the addition of new features in Coq, the tables able to store and restore their contents are given a common interface of so called summaries, implemented in the file summary. The interface specifies three methods freeze function, unfreeze function and init function for storing, restoring and initializing a table respectively, and one boolean value survive section which partially controls the discharge at the end of a section. The important remark here is that the global environment is also registered as a summary and managed through the generic mechanism.

9.3.2 Module operations on objects

To understand the issues we had to solve when putting together modules and extra-logical features of Coq, consider the following code which uses Syntactic Definition as an example.

Coq < Module M.
Coq <   Definition id := [A:Set][a:A]a.
Coq <   Syntactic Definition Id := (id ?).
Coq <   Check (Id O).
(id nat (0))
     : nat
Coq < End M.
Coq < Check (M.Id true).
(M.id bool true)
     : bool
Coq < Module N:=M.

Coq < Check (N.Id (M.Id None)).
(N.id (A:Set)(option A) (M.id (A:Set)(option A) None))
     : (A:Set)(option A)
Coq < Print N.Id.
 Syntactic Definition N.Id = (N.id ?)

When we rebind the module M to a new name N, in the same time we create a new syntactic definition N.Id, which now is a shorthand for (N.id ?). So the same mechanism of substitutions that exists in the kernel for module components had to be implemented for library objects^{9}. This mechanism naturally covers objects defined inside a functor body and used after the functor is applied to a module.

Coq < Module Type SIG.
Coq <   Parameter id : (A:Set)A->A.
Coq < End SIG.
Coq < Module F[X:SIG].
Coq <   Syntactic Definition FId := (X.id ?).
Coq < End F.
Coq < Module M1:=(F M).
Coq < Module N1:=(F N).
Coq < Check (M1.FId O).
(M.id nat (0))
     : nat
Coq < Check (N1.FId true).
(N.id bool true)
     : bool

Moreover, it is possible to attach objects to a module type. Such objects can be used inside a functor body or attached to a module defined with that module type.

Coq < Module Type OBJECTS.
Coq <   Parameter id : (A:Set)A->A.
Coq <   Syntactic Definition Id := (id ?).
Coq < End OBJECTS.
Coq < Module G[X:OBJECTS].
Coq <   Definition id := [A:Set][a:A](X.Id a).
Coq < End G.
Coq < Module X.
Coq <   Definition id := [A:Set][a:A]a.
Coq < End X.
Coq < Module P : OBJECTS := (G X).
Coq < Check (P.Id O).
(P.id nat (0))
     : nat

9Moreover, the same substitution that is used in the kernel, is also used in the library.

The interface of objects has been extended with a new method subst function, and in order to unify the treatment of objects for simple modules, functors and module types, we introduced a concept of object closure. It corresponds to a list of inactive objects that reference module names invalid in the current context, that wait to be substituted once the objects are assigned to an actual module and activated. An object closure is remembered for simple modules because of renamings, for functors because of applications, and for module types because of their possible attachment to modules.

Technically, an object closure is built from a list of objects, a list of bound module identifiers (functor formal parameters), a self identifier and a delayed substitution for efficient handling of partially substituted object closures.

In the rest of this chapter we call objects which behave like our Syntactic Definition substitutive. However, we do not want to force all kinds of objects to have this behavior. Therefore we decided to leave the object writers the choice: either the object must be equipped with a substitution function or it may only refer to “user names” that do not contain module identifiers (see Section 9.2.3). The objects of the latter kind are dropped during the operations which require renamings and therefore are useless in functor bodies and module types.

9.3.3 Interactive modules revisited

When the user starts a new interactive module, Declaremods.start module is called, which itself calls Global.start module to start the module in the global environment and Lib.start module to push a special start mark on top of the library stack, together with the frozen state.

Inside the module, user commands are executed and corresponding objects are pushed on the stack and their cache function methods are called.

When the module is ended, Declaremods.end module is called. Its operations are the following.

1. End the module in the global environment,

2. Get objects and the state from the library stack,

3. Classify objects,

4. Compute the object closure,

5. Prepare the module object,

6. Restore summaries except for the environment,

7. Push the module object on the stack.

The operation on the global environment is performed through Global.end module (point 1). The call to Lib.end module recovers all objects^{10} pushed on the stack after the most recent module has been started and the state of the system stored at that time (point 2).

The objects are divided into four groups according to their classification (by the function Lib.classify segment), one of them is disposed and three of them are kept (point 3). These are substitutive objects, non-substitutive objects and anticipated objects. The latter group are in fact library objects associated with the commands Require and Read.

Next, the object closure of the module is computed (point 4), according to the module specification: If the module was given an output signature when it was started, we take the object closure of the signature. Otherwise the object closure is constructed from the objects recovered in point 2, the list of functor arguments given when the module was started, the self identifier of the module body and the empty substitution.

Now, if the ended module is not a functor, the objects from its object closure are substituted (using the method subst function) with the substitution contained within the closure extended with the mapping of the self identifier to the name of the module. The object closure, the substituted objects and the non-substitutive objects are packaged into a module object (point 5), but before putting it on the command stack the summary operation is performed.

Through the new summary function unfreeze other summaries, all tables except for the global environment are restored (point 6) to the state recovered in point 2.

Only now, the module object is pushed on the stack, just after the anticipated objects and their load function methods are called (point 7). For the module object, this method first of all stores the object closure in the table modtab substobjs and if the module was not a functor, calls load function for every substituted and non-substitutive object. This way if the module has submodules, their objects are activated as well. Both lists of substituted and non-substitutive objects are stored in the table modtab objects which is kept for a possible future Import operation (see below) or other operations requiring searching through objects attached to a module.

9.3.4 Non-interactive modules

Although the operations described in the previous section may seem complicated, our goal was to maximally unify the object operations for interactive and non-interactive modules (possibly nested), for modules loaded from disk, and for module types, having in mind that all these should cooperate with the backtracking mechanism.

Indeed, the management of non-interactive modules is similar to the points 4, 5 and 7 of the end module operation. Let us take

Module M:=F N

10Intermediate copies of the state stored on the stack inside the module are forgotten.

as an example, and suppose that the formal parameter of the functor F is X. The object closure of M is constructed from the object closure of F, by extending the substitution contained within the closure with the mapping from X to N^{11}. Now the module object is prepared in a similar way to the point 5 in the previous section, with the only exception that there are no non-substitutive objects and that the module object contains also the body of the module definition, that will be passed to the kernel.

When the module object is pushed to a stack, the method cache function of the module object is called. It starts by updating the global environment with the function Global.add module and then performs exactly the same operations as the load function called in the end of the end module operation: the object closure is stored in the table

modtab substobjs, the substituted objects in modtab objects, and load function is

called for every one of them.

Theory files are treated like special modules and their object operations are performed by module functions. Interactive and non-interactive module type operations are very similar to those for modules, but simpler, since only the object closure is important and no objects are substituted. For module types there is only one table, modtypetab storing object closures. Also, non-substitutive objects resulting from commands issued inside an interactive module type are simply discarded.

The last module operation affecting library objects is Import. This command declares all components of the module to be directly available at toplevel without any prefix. This command was already available in previous versions of Coq, but concerned theory files only. Now it has been naturally extended to all modules.

Importing a module may also affect activity of some objects even though they have no name available to the user. For example it is natural to expect that parsing or pretty printing rules defined inside a module should be active only if the module is imported.

Coq < Module P.
Coq <   Definition two:=(S (S O)).
Coq <   Notation "# x":=(plus x two).
Coq < End P.
Coq < Eval Compute in (#two).
Toplevel input, characters 17-18
> Eval Compute in (#two).
>                  ^
Syntax error: [test int rparen] or [constr:lconstr] expected after '(' (in [constr
:constr])
Coq < Import P.
Coq < Eval Compute in (#two).
     = (4)
     : nat

11If we had a module renaming, Module M:=N, we would take the object closure of N, and if the output type was given, Module M:SIG:=..., we would take the object closure of SIG.

For that purpose, every object declares the method open function, which implements the action to be performed when the module containing the object is imported. The function import module realizing the command Import, looks up the module objects in

modtab objects and calls open function on each of them.

9.3.5 Long names and suffix visibility

Since version 7 of Coq, all named elements, like definitions, inductive types etc. have unique long names available to the user. For example the long name of the inductive type list from the file PolyList of the standard library is Coq.Lists.PolyList.list. Depending on the way the file PolyList gets loaded in the Coq toplevel, longer or shorter suffixes of this unique long name are also valid qualified identifiers referring to list. After the command Require PolyList all suffixes are valid, and after Read Module PolyList the shortest valid suffix is PolyList.list. Note that in the latter case the command Import PolyList makes the shortest suffix, list, valid as well.

Without modules, suffix validity patterns were very simple: either all suffixes were valid, or all suffixes of length strictly greater than one. In the recent version of Coq, visibility patterns may be more complicated.

After entering the following example (on the left), the validity of suffixes of Top.M.N.P.z becomes (on the right):

       Module M.
         Module N.
           Module P.
             Definition z:=O.
           End P.
         End N.
       End M.
       Import M.N.

                                                      Top.M.N.P.z  valid
                                                      M.N.P.z  valid

N.P.z invalid P.z valid z invalid

Of course suitable Import sequences may lead to arbitrary validity patterns with the only exception that all suffixes containing the leftmost occurrence of a module identifier are valid. In our example M is the leftmost module name, and Top is the logical path, and in the first example of this section, PolyList is the module identifier, while Coq.Lists is the logical path. At the same time, notice that module operations may request the validity of either all suffixes containing the given minimal suffix or of precisely the given suffix.

Validity of suffixes is managed by the file nametab, whose function is to maintain the correspondence between user names and internal names. During the implementation of modules, the file nametab has been substantially cleaned up and its functionality extended.

Fully qualified long names, like Top.M.N.P.z, are represented by one of the two types:

Libnames.section path for simple elements and Libnames.dir path for modules. The

abstract absolute kernel names are represented by the types Names.kernel name and

Name.module path respectively.

In general, nametab implements registration functions and two kinds of translations: from partially qualified user names to kernel names and from kernel names to user names. The latter translation comes in two flavors: the result is either the full user name of the object or the shortest suffix referring to the same object.

All registration functions have a parameter of type visibility, which corresponds to two kinds of suffix validity, possibly requested by module operations.

type visibility = Until of int | Exactly of int

where the information about the given suffix is its length. The length of the corresponding suffix is passed to object methods load function and open function which register the objects name with visibility Until and Exactly respectively.

In the first example of this section, the command Read Module PolyList results in a call to load function of the object representing the inductive type list and the latter registers the name with the visibility Until 2. The consecutive Import results in a call to open function and registration with Exactly 1. In the second example, closing of the module M registers z with Until 4, and Import M.N with Exactly 2.

9.3.6 Summary of library files

Here is the list of more important changes made to files in library due to module introduction

nameops — all definitions moved to libnames; now contains mostly operations on identifiers

libnames — new file: contains definitions of qualified user names moved from the file nameops and the kernel file names

summary — added special treatment for the global environment

nametab — added tables for modules, module types and directories; generally cleaned-up

libobject — added two new methods in object interface: functions classify function

     and substitute function

global — added new functions reflecting changes done in the kernel file safe typing

lib — added functionality connected to interactive modules: new stack nodes, functions for starting and ending modules and module types

declaremods — new file: contains all functions managing extra-logical aspects of modules

library — most operations on the state are now performed through declaremods Figure 9.4: Old (top) and new (bottom) structure of the library

180

9.4 Conclusions

In this chapter we have described the implementation of our module system in the Coq proof assistant. The implementation carefully follows the idea of separating correctnesscritical code (the kernel) from the rest of the system.

Thanks to interactive modules the user can construct parametric theories and certified data structures without losing the advantage of interactive proof development.

The modules have been successfully used in several examples, even though some features, like parsing and printing of structures and signatures and precise error messages are still not implemented.

On the other hand, generic Coq mechanisms of objects and summaries has been adapted to the new modular structure and most user facilities already cooperate with modules in a satisfactory way.

Related work

10.1 Module systems for ML

The module system studied in this thesis, which is an adaptation of MC from [32], is a variant of module systems from the ML family of programming languages.

For the first time modules were introduced in the Standard ML [70] of New Jersey [2] in the mid-eighties [65] and the semantics of modules as dependent records was given in [66]. Since then the module language evolved gradually through the introduction of higher order functors [67] and type abbreviations in signatures [59, 48]. In parallel, the notion of type abstraction was studied in details [72, 17, 16] and modules became the main vehicle of type abstraction in ML [61]. The overly complicated stamp-based semantics of functor application has been simplified using type-theoretical concepts: translucent sums [48, 63] and manifest types [59].

Next, the traditional generative semantics of functors was questionned: applicative functors have been implemented in Objective Caml [60], and in Moscow ML [73] a programmer can choose for each functor whether it will be generative or applicative [89]. Moscow ML implements also modules as first-class objects [89, 90], a feature which has already been studied in [48, 63] and earlier in [71].

Recently, the problem of cross-module recursion is getting more and more interest [36, 40]. One approach consists in using the so-called mixin modules [1, 50], which are partially defined structures with the operation of “mixing” two partial structures, replacing fields undefined in one structure with the definitions from the other and vice versa. Another, more direct approach has been used to implement recursive modules in Moscow ML [91] and Ocaml [51].

The newest features of ML-style modules do not appear to be much wanted for inclusion in proof assistants. The need for these features will probably arise when formal proofs of correctness of programs requiring cross-module recursion will start to be developed using proof assistants. For now, most proof assistants provide modular features comparable to signatures/structures with first order functors.

10.2 Theory management in proof assistants

10.2.1 A module system for ELF

The LF Logical Framework [47] is designed to provide an appropriate language for the high-level specification of deductive systems. The framework is intentionally weak: there is no polymorphism, impredicative constructs etc. This permitted the construction of operational interpretation of LF realized in a programming language Elf [83, 84].

The module system for Elf [49] is built from two principal elements: signatures and realizors, which correspond to first order functors.

Signatures define axioms and rules corresponding to modelled deduction systems. Realizors provide a means of interpreting one signature into another. For each declaration in the target signature, a definition must be provided using the constants from the source signature. Signatures and realizors can be parametrized and instantiated by other realizors.

Signatures cannot have manifest values but these can be expressed by parametrization. On the other hand, the language contains operations to include one signature in the other, making signature inclusion explicit. This feature is usually absent in theoretical descriptions of the ML module language.^{1}

10.2.2 PVS theories

Prototype Verification System PVS [78] is probably the most widely used of all proof assistants. It contains a very expressive specification language based on dependently typed higher order logic with predicate subtyping. Its type-checking is undecidable and hence verifying well-formedness of statements may generate type-checking conditions called TCCs, which have to be proved by the user.

Theories are a very important concept in PVS. Their semantics have been formally defined in [79]. Every development consists of one or more theories. A theory contains three main parts: parameters, assumptions and exports. When the theory is imported its parameters must be explicitely instanciated and assumptions generate TCCs. In return, the definitions and lemmas from exports can be used freely.

Speaking in the module language, a PVS theory may be interpreted as a first order functor, whose input signature consists of parameters and assumptions of the theory and output structure is the exports part. However, assumptions and exports may include other theories, which corresponds to inclusion of signatures and structures.

10.2.3 Maude’s module algebra

The module system implemented in Maude [69] is described in [41]. Its semantics is presented using the powerful categorical notion of institutions [46].

Its main ingredients are modules, theories and views. A module is a specification with initial or free algebra model. A theory is a specification with a “loose” model, i.e. any algebra verifying the specification is a model of the theory. Views are theory interpretations used to instantiate parameter theories, refine specifications and assert formal properties.

1However, signature and structure inclusion is implemented in Ocaml.

In the theoretical approach all three concepts can be parametrized and instantiated by theories, but implementations provide only instantiation of modules.

We can say that Maude modules roughly correspond to structures and functors from the ML module language (they are precisely defined), theories to signatures and parametric signatures (they specify a class of structures) and views, being algebraic signature morphisms, correspond to coercions with the possibility of renaming. They replace signature subtyping from the ML module language.

10.2.4 Isabelle

The main concept for structuring mathematical developments in Isabelle [52] is a theory. Theories organize the syntax, declarations and axioms of the mathematical development. They are built by extending and merging existing theories (starting from one or two built-in ones).

Isabelle provides two more means to structure mathematical developments: locales and axiomatic type classes. Locales [56] are mostly concerned with convenient management of assumptions. They correspond to Coq sections with extended functionnality. It is possible to reopen an already closed locale, have several open locales at the same time or interactively decide to abstract a given theorem with respect to the local assumptions. A proof of Sylov theorem [57], depending on many layers of algebraic structures, showed the usefulness of the new concept.

Another construction is that of axiomatic type classes [75]. They resemble Haskell type classes and provides very lightweight notation for operations and properties of the given type and its derivatives (like product, functional type etc.) This functionnality is strictly connected with a particular type, so it is for example not possible to consider at the same time the additive and the multiplicative monoids issued from a given ring (both declared as a monoid type class). From the theoretical point of view much of the functionnality of type classes can be simulated by first order functors, but practically such a simulation would mean to write a functor for each class and write a module for each instance of that class, apply the functor to the instance etc.

10.2.5 Dependent records

Dependent records have been proposed as a means to represent mathematical structures in [10, 9, 8]. Unlike in all previous approaches, structures are first class anonymous values, so there is no need to consider a particular notion of parametrization. This first proposal has been extended with manifest fields [86] and showed to be consistent by encoding in the type theory of inductive-recursive definitions.

Recently, dependent records with manifest fields have been presented in a logical framework based on the Martin-Löf type theory [30]. The semantics of the records is built using a PER model and the correctness of subtyping is proved using the fact that subtyping is a general form of η-expansion.

Dependent records have been implemented in Agda [25] and are used in a similar way to SML modules.

10.2.6 Discussion

To summarise the presented examples, we can say that many proof assistants provide a functionality similar to modules and functors with various syntactic facilities. Often inclusion of signatures and structures is provided which is currently missing in Coq.

As far as rewriting is concerned, the difficulty of adding it mostly depends on the nature of a tool, the logic, the treatment of equality rather than the structuring feature. In Maude rewriting is an inherent part of the logic and the implemented system of named modules has no interactions with it. In PVS and Isabelle the correctness of the system does not depend on the termination and confluence of reductions. At worst, a tactic invocation will loop and will have to be interrupted by the user.

From the structuring facilities decribed above, only Isabelle locales and dependent records are different from our modules in important aspects. Problems related with locales and rewriting are exactly the same as with Coq sections and rewriting. The easier part concerns abstracting definitions by rewriting with respect to local variables and assumptions and the harder part concerns allowing local definitions by rewriting and abstracting constants and theorems with respect to those definitions, which seems out of reach for current normalisation and confluence techniques.

Adding definitions by rewriting to dependent records would have the same negative effect as adding them to second-class anonymous modules (see Section 1.6 for discussion).

Satisfying closure properties

In this chapter we discuss side-conditions for formation rules for inductive types and rewriting and closure properties they have to satisfy. This concerns typing rules (Ind-Spec/Form) with the side-condition POS, (RewSpec/Form) with the side-condition ACC and (RewSpec/Rew) with the side-condition COMP.

The role of the first two conditions is to assure that type-checking in a correct environment is decidable, and an environment is correct only if all definitions by rewriting have been checked by the condition ACC and all inductive definitions have been checked by the condition POS.

The third condition is supposed to assure that any formal development is consistent. By a formal development we mean a structure well-typed in the empty environment (see Definition 3.1.2). A structure is well-typed only if all definitions by rewriting, using Rew as an implementation, have been checked by the condition COMP (in addition to ACC).

In particular, this condition is supposed to guarantee, that the proposition t in the resulting structure in Theorem 1 in Chapter 3 is not False.

Our work shows, that given such conditions satisfying five closure properties defined in Section 2.7.1, if they indeed imply decidability of typing in an environment without modules,^{1} then typing terms in any environment (with modules) is decidable.

Similarly, if the condition ACC guarantees consistency of a formal development without modules^{2} then it also guarantees consistency of any formal development (with modules).

Below we discuss the closure properties that have to be satisfied by the acceptance conditions. Before going into the details it is important to remark that judgments in our calculus are also closed by operations defined in closure properties, i.e. E ⊢ J implies E′ ⊢ J′ where E and E′ are as in the closure properties and the relation between J and J′ is the same as between Ω and Ω′.

This means in practice that if an acceptance criterion requires performing some additional typing or convertibility tests on terms, they do not affect satisfaction of closure properties. Indeed, all judgments in our calculus are closed by substitution of paths to variables (Lemma 4.1.5) and by environment extension (Lemma 4.1.4) and all term judgments are closed by module removing operations (Lemmas 8.3.3, 8.3.4 and 8.3.5).

1In fact it is enough to show decidability of typing in a classic environment, i.e. without modules and without manifest block specifications.

2idem for consistency.

In the rest of this chapter we assume that a rewrite reduction ⟶_{R} and delta reduction ⟶_{δ} are defined by orienting judgments obtained from the rule (P/RewSpec/Comp) and (P/Eq/Comp) respectively:

\frac{E ⊢_{P} \overline{p} : RewSpec(E^{Γ}, R) θ : E → E_{i}}{E ⊢ e_{i}^{L}\{v_{j} ↦ p^{j}\}θ ⟶_{R} e_{i}^{R}\{v_{j} ↦ p^{j}\}θ}

\frac{E ⊢_{P} \overline{p} : Eq(\overline{ϕ} : Φ)}{E ⊢ p^{i} ⟶_{δ} ϕ^{i}}

It is important to remark that reductions defined as above are closed by module removal operations, because judgments in their premises are.

Property (C1). The first closure property is substitutivity of a variable by an access path. Taking the ACC condition as an example, this property is:

ACC_{E}(E^{Γ}, R)  implies  ACC_{E′}(E^{Γ}δ, Rδ)

where δ = \{\overline{p} ↦ \overline{v}\} and

E = E_{1}; \overline{v} : S; E_{2}  E_{1} ⊢ \overline{p} : S  E′ = E_{1}; E_{2}δ

This is not an unexpected property, but requires authors of acceptance criteria to be quite careful.

Let us start by a remark that closure by substitution of terms (and not only access paths) to variables, is verified by typing judgments in just about any reasonable calculus: E ⊢ J implies E′ ⊢ Jδ. This property is necessary to prove correctness of type of an application or subject reduction property for β.

In the calculus with modules, substitutivity is used for example to prove correctness of type of a module application. In the presence of module subtyping and satisfaction rules for block types substitutivity is substantially “harder” in some aspects than in a calculus without modules, even though we substitute only a restricted class of expressions, i.e. access paths.

To see the difficulty let us present a simple example. Let E and E′, E_{1} (names matching (C1) above, E_{2} empty) be defined as follows

E = E_{1}; X : Ty(SIG_{X})  E′ = E_{1}  E_{1} = Ê; N : Ty(SIG_{N}); Ẽ

for some Ê and Ẽ and the following signatures SIG_{X} and SIG_{N}:

SIG_{X} = Sig
          Symbol
            g : nat → nat
            f : nat → nat
          Rules
            g (f O) ⇒ f O
        End

                                SIG_{N} = Sig

                                          Definition f : nat → nat := [x:nat]x
                                          Symbol
                                            g : nat → nat
                                          Rules
                                            g O ⇒ O
                                        End

which in the formal syntax is

SIG_{X} = Sig g, f : Ty(RewSpec(E_{gf}, R_{gf})) End
SIG_{N} = Sig f : Eq([x:nat]x : nat → nat); g : Ty(RewSpec(E_{g}, R_{g})) End

where

E_{gf} = g : Ty(nat → nat); f : Ty(nat → nat) R_{gf} = ϵ | g (f O) ⟶ f O : nat
E_{g} = g : Ty(nat → nat)  R_{g} = ϵ | g O ⟶ O : nat

Let us show that the substitution δ = {X ↦ N} is legal or, in other words, that the judgment E_{1} ⊢ N : Ty(SIG_{X}) holds.

Indeed, in the environment E_{1} enriched with the contents of the signature SIG_{N}, we can show that g (f O) ≈ f O using the fact that f is specified to be equal [x:nat]x. Next, using the rule (RewSpec/Sat), we can show

E_{1}; f : Eq([x:nat]x : nat → nat); g : Ty(RewSpec(E_{g}, R_{g})) ⊢ g, f : RewSpec(E_{gf}, R_{gf})

which by the (Sig/Sub) gives E_{1} ⊢ SIG_{N} <: SIG_{X}. Now it is enough to apply the rules (Subsumption) and (Ty/Sat) to get E_{1} ⊢ N : Ty(SIG_{X}) as expected.

In this context, by property (C1), ACC_{E}(E^{Γ}, R) should imply ACC_{E′}(E^{Γ}δ, Rδ) for any E^{Γ} and R. In particular if R is defined to contain one rule

h (X.g O) ⟶ S O

which is both confluent and terminating, Rδ becomes non-confluent because of the nonjoinable critical pair h O = S O. The counter-example to termination preservation can also be found very simply. It is enough that SIG_{X} contains the rewrite rule f (g x) ⟶ g (f x), and SIG_{N} its opposite, i.e. g (f x) ⟶ f (g x). Given R equal

h (X.f (X.g x)) ⟶ h (X.g (X.f x))

it is both confluent and terminating, unlike Rδ which is non-terminating.

The simple means to get rid of at least confluence problems provoked by the condition (C1) is to require that the left-hand sides of rules in R contain only symbols defined in E^{Γ}. The exception could be made for constructors of inductive types of course. This restriction would make R mutually orthogonal with all other rewriting systems defined in the environment and its future extensions.

Property (C2). The second closure property requires the acceptance criteria to be closed by environment extension.

ACC_{E}(E^{Γ}, R)  implies  ACC_{E′}(E^{Γ}, R)

where

E = E_{1}; E_{3}  E′ = E_{1}; E_{2}; E_{3}

Closure by environment extension holds for typing judgments in most formal systems and is absolutely required for any system of “loadable environment fragments”, ready to use without rechecking: E ⊢ J implies E′ ⊢ J. In particular, the system of separately checked theory files, as implemented in Coq until version 7.3 also required this property to be fully flexible.^{3}

For definitions by rewriting, this property simply means modularity. Even though modularity is far from being trivial for rewriting [77, 39], this property cannot be avoided in any proof assistant permitting separate checking of fragments of developments. Note that at least for confluence, modularity is ensured by orthogonality proposed for the condition (C1).

Properties (C3), (C4), (C5). These properties require that acceptance criteria should be closed by module removal operations: flattening of signatures, functor removal and elimination of manifest modules.

In other words they say that acceptance criteria should not be “aware” of modules. It is actually hard to think of sensible criteria verifying the first two properties but not this one. Conditions breaking them are judgments about modules themselves and not their term or block components, or something like “there is no more than one symbol defined by rewriting in the module X”, but it is hard to see why anyone would need this kind of conditions.

Example. We show that there exist acceptance criteria for the calculus of constructions with inductive types and definitions by rewriting which satisfy our closure properties. As stated before it is enough to show that these criteria imply decidability and consistency of the calculus without modules and without manifest blocks.

A positivity condition for inductive types POS(E^{I} := E^{C}) can be found for example in [24]. It limits the syntactic form of kinds for inductive types from E^{I} and types of constructors from E^{C}:

• kind of an inductive type must be a product whose co-domain is a sort,

• type of a constructor must be a product whose co-domain is an instance of an inductive type from E^{I} and whose arguments contain only strictly positive occurrences of inductive types from E^{I}.

This condition obviously satisfies closure properties (C1)-(C5). It even satisfies general substitutivity (by terms) as inductive definitions are usually studied as anonymous entities [80, 100, 94].

3Actually, due to universe constraints this does not hold in Coq, but since verifying universe consistence can be done quickly, it is verified after every module and file operation.

The acceptance condition for rewrite rules, ACC(E^{Γ}, R), can be based on a modular termination criteria such as Higher Order Recursive Path Ordering [99] or the General Schema [12]. Both these termination criteria, denoted by T C in the sequel, are based on a partial order on the set of function symbols, called precedence, and guarantee the termination of the relation ⟶_{R} ∪ ⟶_{β} for every accepted rewriting system R. Both these criteria are closed by extensions of the set of function symbols, provided that the new precedence is an extension of the old one. The acceptance criterion ACC(E^{Γ}, R) is defined as follows: A definition by rewiring (E^{Γ}, R) is accepted in the environment E iff the following conditions hold:

1. left-hand sides of rules in R are linear and algebraic, and contain only function symbols from E^{Γ}, constructors from E and local rule variables,

2. R is accepted by T C with the precedence “E < E^{Γ}”, meaning that

• all function symbols (valid term paths) from E are incomparable with each other,

• symbols in E are smaller than variables in E^{Γ},

• symbols from E^{Γ} are ordered by some ordering <_{E^{Γ}},

3. the critical pairs of R are joinable (by R).

The first condition is very restrictive. In practice it means that all related definitions by rewriting are put in one place. For example if addition on natural numbers is defined in one module, and multiplication in the other, the latter definition cannot contain the distributivity law presented as a rewrite rule.

To explain the correctness of the above criterion, let us start by showing that it satisfies our closure properties.

Condition 1 obviously satisfies properties (C2)-(C5). It does also satisfy (C1), because a path substitution can only influence constructors. Fortunately Lemma 8.1.5 shows that one can derive that a sequence of access paths is an inductive definition only if it has been defined as an inductive definition in the environment and Lemma 8.1.4 shows that this property is closed by substitution. Hence, a constructor remains a constructor after a substitution.

Condition 2 satisfies (C1), because there is no assumption about the relative order of symbols in E, so any substitution has no effect on the comparisons between symbols. The property (C2) follows by the fact that T C is stable by ordering extension. Properties (C3)-(C5) are satisfied trivially.

Condition 3 satisfies all closure properties, because all confluence diagrams for critical pairs remain valid after substitution.

Given a correct environment E, the termination of the relation ⟶_{β} ∪ ⟶_{R_{E}}, where R_{E} is the set of all rewrite rules in E, follows from the properties of T C. Indeed, the way a correct environment is built by adding subsequent rewriting systems corresponds to the notion of modularity from [12] and [99].

Since T C guarantees termination of ⟶_{R_{E}} ∪ ⟶_{β}, confluence of ⟶_{R_{E}} follows from the fact that it is locally confluent. Indeed, all critical pairs issued from a single block are joinable by assumption, and no critical pairs exist between blocks. Confluence of ⟶_{R_{E}} ∪ ⟶_{β} can be proved using the results from [74]. In the end we get a decidability of convertibility which results in a typing algorithm.

The problem of consistency for the calculus of constructions with rewriting has not been very well studied yet. The only condition that we know that guarantees consistency [12] is not modular. However, it is believed that completeness of definitions as defined in [58, 27, 15, 18], requiring that all constructor instances of a function symbol are reducible, can be extended to the calculus of constructions and proved to guarantee consistency. Such a condition COMP(E^{Γ}, R) would certainly satisfy closure properties (C1)-(C5), as constructors do not change under substitutions.

Conclusions and future work

12.1 Conclusions

We have presented an ML-style module system for a proof assistant based on type theory. In our formalism modules are named entities and therefore well-suited to contain generative definitions such as inductive types and definitions by rewriting. Given modular acceptance criteria for these definitions, we have shown that the typing system with modules is a conservative extension over the typing system without modules and that decidability of the latter implies decidability of the former.

Our theoretical work is parameterized by criteria of acceptance of inductive definitions and definitions by rewriting. We have formulated 5 closure properties which must be satisfied by these criteria in order to make our proofs work. These properties may be shortly described as modularity and closure by substitutions of names to names. They obviously hold for positivity conditions for inductive types (as described in [80, 100, 94, 24]) and are reasonable for termination and confluence criteria for definitions by rewriting. We give a proposition of such criteria in Chapter 11, but finding really flexible criteria satisfying our closure properties is left as a future work.

We have successfully implemented our module system in the released version 7.4 of Coq. As the theory shows, the future planned extension of Coq with rewriting will not conflict with modules.

The modules have already been successfully used in several examples. In combination with the extraction mechanism capable to produce Ocaml modules from Coq modules (implemented by Pierre Letouzey), Coq may become a very convenient tool to develop certified parametric data structures. For example, a modular formalization of certified dictionaries implemented by binary search trees was realized by Pierre Casteran (contribution

Bordeaux/dictionaries).

Jean-Christophe Filliâtre and Pierre Letouzey have certified [43] a parametric realization of red-black balanced binary trees and the version of AVL trees implemented in the Ocaml Standard Library. A long-standing bug in the balancing of AVL trees has been found and corrected thanks to this formal development.

Another interesting example of using modules is Krakatoa [68], a Coq frontend to certify Java programs. The authors prove quite a few generic properties of Java classes abstractly in a functor, and a user working on a concrete Java program simply applies the functor to a module representing the classes of his program and can conveniently use the properties already instantiated to his particular setting.

12.2 Future work

The immediate continuation of the work presented in this thesis in polishing the implementation of modules in Coq. Other tasks include solving practical problems related to the implementation of rewriting and extending the module system.

Polishing the implementation. Even though the implementation of modules is fully usable, there are still some corrections to be made. The first one concerns error messages which are often quite cryptic. It seems necessary to reimplement error reporting for modules in such a way that the messages are clearer and positions of errors are shown to the user.

Another deficiency of the current implementation is incomplete parsing and prettyprinting of module expressions. Indeed, there is no way to enter a whole structure or signature at once.^{1} Also printing of structures and signatures is very superficial: only the names and kinds of module components are printed without constant bodies or type information. The problem however is not completely trivial as parsing and pretty-printing are context dependent and require parallel processing of logical (definitions, lemmas, etc.) and extra-logical features (implicit types, pretty-printing and parsing rules etc.)

The last imperfection of the implementation mentioned in this section is the incomplete subtyping for inductive definitions. In theory

Sig bool, true, false : Ty(IndSpec(E_{bool}^{I} := E_{bool}^{C})) End
<:
Sig bool : Ty(⋆); true : Ty(bool); false : Ty(bool) End

while in the implemented module system it is not the case. The problem comes from the fact, that the term representing an inductive type bool is different from the term representing a constant bool. Therefore a substitution resulting from the subtyping judgment presented above must change the structure of terms and not only names appearing in terms. This is very unfortunate as the same substitution is used inside and outside the kernel and the fact that it modifies only names simplifies greatly the implementations.

Two solutions are therefore possible: a dirty hack or a complete change of representation of inductive types and constructors, requiring tedious modification of a few hundred places where the current structure of terms is explicitly used.

1Of course any module expression can be entered using interactive modules and interactive module types. It is only a matter of user convenience.

New features. The most important feature of modules which is missing compared to Ocaml is a possibility to have module interfaces (signatures) in separate files. This would help doing large proofs in groups of many people, once they agree on the interface of a proof library, one person could go on proving properties from the library and the other could start using the library, much before all the proofs are done.^{2} This would require writing a utility verifying that all bodies of components of a big project do match their interfaces. Such a utility would play a role of a linker which links together all related modules of a big program.

Another interesting feature wish, signaled by users, is the possibility to create multifile functors. It should be possible to write the input signature of a functor in one file, the output signature in another, and work with several files containing proofs until they are completed.

Practical problems with rewriting. The obvious continuation of the work presented in this thesis is solving the practical issues arising when putting rewriting and modules together in Coq.

In particular, our decidability proof for the typing problem for the calculus with modules shows a very inefficient algorithm: translate everything to the calculus without modules and without manifest blocks, and solve the problem there (which is assumed to be feasible). It is therefore necessary to find a direct approach, using oriented delta and rewrite reductions to decide convertibility in the calculus with modules and manifest blocks. This is closely related to the problem of finding flexible acceptance criteria for rewrite rules, assuring termination and (some form of) confluence in the presence of delta rules.

It appears that precedence-based termination criteria such as General Schema [12] or Higher-Order Recursive Path Ordering [99] could easily be used to prove termination of the relation ⟶_{R} ∪ ⟶_{β} ∪ ⟶_{δ}. Indeed, the delta reduction is derived from the judgment E ⊢_{P} \overline{p} : Eq(\overline{ϕ} : Ψ), and necessarily \overline{ϕ} is defined before \overline{p} in the environment so it is smaller than \overline{p} in the precedence. Hence the rewrite rule p^{i} ⟶_{δ} ϕ^{i} should be accepted.

The confluence of ⟶_{R} ∪ ⟶_{δ} is more complicated though. In the presence of the restriction on function symbols in the left-hand sides of rewrite rules, presented in our proposition for the ACC condition in Chapter 11, delta reduction on symbols defined by rewriting should not be a problem. Given a manifest definition by rewriting \overline{v} : Eq(\overline{p} : RewSpec(E^{Γ}, R)) the symbols \overline{v} can in the same time δ-reduce to \overline{p} and reduce using the rules in R, resulting in critical pairs of the form

e^{L}[p^{j}]_{q} _{δ}⟵^{q} e^{L} ⟶_{R} e^{R}

Since \overline{p} : RewSpec(E^{Γ}, R), we have e^{L}\{\overline{v} ↦ \overline{p}\} ≈ e^{R}\{\overline{v} ↦ \overline{p}\} and e^{L}[p^{j}]_{q} ⟶_{δ}^{∗} e^{L}\{\overline{v} ↦ \overline{p}\} and e^{R} ⟶_{δ}^{∗} e^{R}\{\overline{v} ↦ \overline{p}\}, so under condition that e^{L}\{\overline{v} ↦ \overline{p}\} ≈ e^{R}\{\overline{v} ↦ \overline{p}\} is joinable, so is our critical pair.

Unfortunately δ-reductions for constructor symbols can create non-confluent critical pairs with rewrite rules, so in order to have efficient algorithm for deciding convertibility, one must perform reductions modulo δ-equality for constructors. This does not appear to be difficult, but should be studied in more details.

2This is of course all possible now, but at the cost of replacing axioms by hand with the proved lemmas.

Another interesting topic is rewriting modulo associativity and commutativity. From the theoretical point of view our proofs already cover this case, since we do not assume that the equations issued from rewrite specifications are oriented. Practically, implementing an efficient reduction engine covering rewriting modulo AC and beta reduction requires quite a lot of work.

This research, combined with experiments with the “Rewriting” branch of Coq should also give much insight on the directions in which the module system should be tuned to make the life of rewriting implementors easier.

Weakening the satisfaction rule. One suggestion that already came from the author of the aforementioned experimental branch of Coq, Frédéric Blanqui, is to weaken the satisfaction rule for RewSpec(E^{Γ}, R). This would make the substitutivity of names (condition (C1)) much easier to achieve, meaning that more rewrite rules would be accepted. The current satisfaction rule, allowing arbitrary conversion between the left and right-hand side of rules in R, seems to make proving confluence more difficult than if only oriented reductions from the left-hand side to the right-hand side were allowed, i.e. if the condition e^{L} ≈ e^{R} were replaced by e^{L} ⟶^{∗} e^{R}.

This modification appears to be feasible, but weakening the RewSpec satisfaction rule would weaken the module subtyping substantially, so there is a trade-off between allowing more “rewriting” or more “subtyping” and the right compromise can only be found through experiments.

Special treatment of completely defined symbols. We showed in Chapter 11, that while considering a rewriting specification RewSpec(E^{Γ}, R), allowing symbols from outside of E^{Γ} in the left hand-side of rules in R is problematic as far as the closure property (C1) is considered.

But it is even worse. In presence of module subtyping such rules are dangerous for consistency. Consider the following piece of Coq code:^{3}

  Module Type SigX.
    Parameter a:nat → nat.
  End SigX.
  Module F[X:SigX].
    Module Type SigM.
      Parameter f:nat → nat.
      Lemma fs : (x:nat)(f (S x))=(1).
      Lemma fa : (x:nat)(f (X.a x))=(0).
    End SigM.

3This example, together with the final theorem (0)=(1) has been checked in the experimental “Rewriting” version of Coq.

    Module M : SigM.
      Symbol f : nat → nat.
      Rules [x:nat] { (f O) => (0); (f (S x)) => (1); (f (X.a x)) => (0) }.
      Lemma fs : (x:nat)(f (S x))=(1).
        Intros; Reflexivity. (* using the second rule *)
      Qed.
      Lemma fa : (x:nat)(f (X.a x))=(0).
        Intros; Reflexivity. (* using the third rule *)
      Qed.
    End M.
  End F.
  Module X <: SigX.
    Definition a:=[x:nat](1).
  End X.
  Module N := F X.

Now the type of N.M.fa is (x:nat) (N.M.f (X.a x))=(O) and using the definition of X.a

and Lemma N.M.fs, we can show that (x:nat) (N.M.f (X.a x))=(1).

Note that there is no critical pairs while checking the rewriting system inside M and since this system is hidden by the signature constraint, there is no critical pairs after applying F to X either.

We therefore found a proof of (0)=(1) using the rewrite rule (f (X.a x)) => (0). Indeed, this rule is wrong, because it implicitly assumes that (X.a x) is O while this is not declared in the functor argument signature.

Having seen the examples in Chapter 11 and the one above, it appears necessary to disallow “foreign” symbols in the left-hand side of rewrite rules. On the other hand it is of course desirable to have the possibility of defining rewrite rules with foreign symbols on the left-hand side, since for example many arithmetic functions f could benefit from rewrite rules of the form f(a + b) ⟶ … and it is not very convenient to be forced to declare them all in one definition by rewriting, together with the function +.

We suspect that the main difference between the example presented above and the rules f(a + b) ⟶ … is the fact that + is completely defined. Going back to our example, if X.a were completely defined at the point of checking our questionable rule, we would get a critical pair. If this critical pair were non-confluent, the questionable rule would be immediately rejected. And if the critical pair were confluent, the proof of (0)=(1) we did would be impossible, and accepting this rule would probably be harmless.

Clearly, more work is needed on completeness of rewriting and in general on definitions by rewriting in an interactive setting.

Proving the rules correct. One approach to proving consistency of the calculus of constructions with rewriting consists in allowing rewrite rules to be added to the system only after the corresponding equality has been proved. This approach seems very reasonable but has not yet been proved to be correct. This is the subject of the ongoing PhD program of Nicolas Oury.

Since this approach concerns adding more rewrite rules to an existing definition it does not immediately fit into our framework. However, the silent assumption that E^{Γ} is nonempty is not really used in our proofs. So adding rules without defining new symbols is not really outside our formalism. But on the other hand, we like to treat our work as a necessary restriction of a hypothetical anonymous system (described in Section 1.6). In that context it is really hard to think about anonymous definitions by rewriting not defining any symbols.

Putting aside these philosophical questions, it also seems possible to treat the proof of equality as an element of the ACC condition verification and therefore accept it in our system provided that the new rule and the old definition by rewriting are in the same module.

Once the approach presented above is proved to be correct, one can consider an interesting syntactic sugar consisting in using functors. First, we define a functor F whose input signature F input contains functions defined by many rewrite rules and whose body uses the rewrite rules to make proofs in a convenient way.

  Module Type F_input.
    Symbol g : nat → nat → nat,  h:nat → nat
    Rules
      g O O => ...
      g (h x) x => ...
      h (g O y) O => ...
  End F_input.
  Module F[X:F_input].
    Lemma lem1 : (x:nat) (g (h x) O) = (h O).
    ...
  End F.

Second we define a structure M containing function definitions and prove equalities corresponding to the rules in F input.

  Module M.
    Definition g := plus.
    Definition h := S.
    Lemma rule1 : (g O O) = ...
    Lemma rule2 : (x:nat) (g (h x) x) = ...
    Lemma rule3 : (x:nat) h (g O y) O = ...
  End M.

And third, we apply the functor F to the structure M, obtaining lemmas about functions in M proved using the rewrite rules defined in F input.

  Module M_lemmas := F M.

Note however, that if the correctness of the output module type of F depends on the rewrite rules in F input, the output module type of M lemmas can be incorrect. It can happen very easily in the presence of dependent types, so it would be necessary to check the correctness of the module type resulting from the module application. This would mean a transformation of the current module application rule into

\frac{E ⊢ p : Funsig(v : M_{1}) M_{2} E ⊢ p′ : M_{1} E ⊢ M_{2}\{v ↦ p′\} : modtype}{E ⊢ p p′ : M_{2}\{v ↦ p′\}}

where the third premise verifies that the application yields a legal module type.

In fact the “Rewriting” branch of Coq already realizes this kind of post-application check, verifying critical pairs of rewrite rules generated by the substitution and rejecting the application if the critical pairs are not joinable. But this resulted most from a coincidence rather than a thoughtful decision.

Quotient types. Even though we have two kinds of blocks, for inductive definitions and definitions by rewriting, nothing prevents us from considering other kinds of blocks, such as definitions of quotient types [6, 35, 34].

Formation rules for these new blocks would have to satisfy the same closure properties in order to match the module system. It can be conjectured that in order to successfully go through with our proofs either there should be no satisfaction rule for such blocks or the satisfaction rule should not require more than what can be derived from its conclusion by elimination and computation rules.

An applicative module system. Our module system puts the same syntactic restriction on access paths to those present in [59]. However, one also could think about relaxing this restriction slightly and put the one described in [60]. This would mean allowing access paths to contain path applications, as in (M.F M.N).P. A module system implemented in Ocaml allows this kind of access paths, but they are of course only valid for type paths and not for value paths.

From the theoretical point of view proving conservativity with such restriction should not be too difficult. It would be enough to add phase zero to our two phase proof, extracting all path applications occurring in terms and giving each of them a name. This procedure is shown to be correct in [61] and in our case it would yield a structure ready for our phase one: evaluation.

The relative decidability proof also seems to be feasible. The problem lies in the implementation of this module system in Coq especially when extra-logical features are concerned such as implicit arguments, pretty-printing and parsing rules etc. In the current implementation, all these extra-logical objects are pre-computed for all legal module paths. In presence of applicative modules the set of all legal module paths can easily be infinite, so this approach is not possible.

With the relaxed restriction on paths, proving termination or confluence of rewriting could also be more difficult. In particular the set of all rewrite rules derivable from the environment can easily turn out to be infinite.

EML methodology. An interesting possibility opened by addition of modules is the adaptation to Coq of the EML methodology of gradual construction of functional programs [92], which strongly relies on the use of functors. In a proof assistant framework, correctness of refining steps could be established formally, using interactive proofs.

Bibliography

[1] Davide Ancona and Elena Zucca. An algebraic approach to mixins and modularity. In M. Hanus and M. Rodrìguez Artalejo, editors, 5th Intl. Conf. on Algebraic and Logic Programming, number 1139 in Lecture Notes in Computer Science, pages 179–193, Berlin, 1996. Springer-Verlag.

[2] Andrew Appel and David B. MacQueen. Standard ML of New Jersey. In J. Maluszynski and M. Wirsing, editors, Programming Language Implementation and Logic Programming, Proceedings of the 3rd Intn’l Symposium, volume 528 of Lecture Notes in Computer Science, pages 1–13, 1991.

[3] Franco Barbanera, Maribel Fernández, and Herman Geuvers. Modularity of strong normalization in the algebraic-λ-cube. Journal of Functional Programming, 7(6):613– 660, 1997.

[4] Henk Barendregt. Introduction to generalized type systems. Journal of Functional Programming, 1(2):125–154, 1991.

[5] Henk Barendregt. Lambda calculi with types. In S. Abramsky, D. M. Gabbay, and T. S. E. Maibaum, editors, Handbook of Logic in Computer Science, volume 2, chapter 2, pages 117–309. Oxford University Press, 1992.

[6] Gilles Barthe and Herman Geuvers. Congruence types. In H. Kleine Buening, editor, Proceedings of the Conference on Computer Science Logic, volume 1092 of Lecture Notes in Computer Science, pages 36–51. Springer-Verlag, 1995.

[7] Stefano Berardi. Type dependence and constructive mathematics. PhD thesis, University of Torino, Italy, 1990.

[8] Gustavo Betarte. Dependent Record Types and Algebraic Structures in Type Theory. PhD thesis, PMG, Dept. of Computing Science, University of Göteborg and Chalmers University of Technology, 1998.

[9] Gustavo Betarte. Proof reutilization in Martin-Löf’s logical framework extended with record types and subtyping. Technical Report 00-09, InCo-Pedeciba, September 2000.

[10] Gustavo Betarte. Type checking dependent (record) types and subtyping. Journal of Functional Programming, 10(2):137–166, March 2000.

[11] Frédéric Blanqui. Théorie des Types et Récriture. PhD thesis, Université Paris-Sud, 2001.

[12] Frédéric Blanqui. Definitions by rewriting in the Calculus of Constructions. Mathematical Structures in Computer Science, 2003. To appear.

[13] Frédéric Blanqui, Jean-Pierre Jouannaud, and Mitsuhiro Okada. The Calculus of Algebraic Constructions. In P. Narendran and M. Rusinowitch, editors, 10th International Conference on Rewriting Techniques and Applications, volume 1631 of Lecture Notes in Computer Science, Trento, Italy, July 1999. Springer-Verlag.

[14] Frédéric Blanqui, Jean-Pierre Jouannaud, and Mitsuhiro Okada. Inductive data type systems. Theoretical Computer Science, 272(1–2):41–68, 2002.

[15] Adel Bouhoula and Jean-Pierre Jouannaud. Automata-driven automated induction. In Twelfth Annual IEEE Symposium on Logic in Computer Science, pages 14–25, Warsaw, Poland, June 1997. IEEE Comp. Soc. Press.

[16] Luca Cardelli and Xavier Leroy. Abstract types and the dot notation. In M. Broy and C. B. Jones, editors, Proceedings IFIP TC2 Working Conference on Programming Concepts and Methods, pages 479–504. North-Holland, 1990. Also available as research report 56, DEC Systems Research Center.

[17] Luca Cardelli and Peter Wegner. On understanding types, data abstraction, and polymorphism. ACM Computing Surveys, 17(4):471–522, 1985.

[18] Jacek Chrząszcz. Complétude de systèmes de réécriture d’ordre supérieur. Université de Paris-Sud, July 1997. Mémoire de stage de DEA.

[19] Jacek Chrząszcz. Implementation of modules in the Coq system. In D. Basin and B. Wolff, editors, Proceedings of the Theorem Proving in Higher Order Logics 16th International Conference, volume 2758 of Lecture Notes in Computer Science, pages 270–286, Rome, Italy, September 2003.

[20] Alonzo Church. A formulation of the simple theory of types. Journal of Symbolic Logic, 5:56–68, 1940.

[21] Horatiu Cirstea and Claude Kirchner. The rewriting calculus — Part I and II. Logic Journal of the Interest Group in Pure and Applied Logics, 9(3):427–498, May 2001.

[22] Horatiu Cirstea, Claude Kirchner, and Luigi Liquori. The Rho Cube. In Furio Honsell, editor, Foundations of Software Science and Computation Structures, http://

www.disi.unige.it/etaps2001 ETAPS’2001, http://www.springer.de/comp/

lncs/index.htmlLecture Notes in Computer Science, pages 166–180, Genova, Italy, April 2001. Springer-Verlag(c).

[23] The Coq proof assistant. http://coq.inria.fr/.

[24] The Coq Development Team. The Coq Proof Assistant Reference Manual – Version V7.4, 2003.

[25] Catarina Coquand. Agda, 2000. http://www.cs.chalmers.se/~catarina/agda/.

[26] Thierry Coquand. An analysis of Girard’s paradox. In Proceedings of the First Symposium on Logic in Computer Science, pages 227–236, Cambridge, Massachusetts, USA, June 1986. IEEE Comp. Soc. Press.

[27] Thierry Coquand. Pattern matching with dependent types. In Proceedings of the Workshop on Types for Proofs and Programs, pages 71–83, Båstad, Sweden, 1992.

[28] Thierry Coquand and Jean H. Gallier. A proof of strong normalization for the Theory of Constructions using a Kripke-like interpretation. In G. Huet and G. Plotkin, editors, Preliminary Proceedings of the First International Workshop on Logical Frameworks, pages 479–497, Antibes, France, 1990.

[29] Thierry Coquand and Gérard Huet. The calculus of constructions. Information and Computation, 76:95–120, February 1988.

[30] Thierry Coquand, Robert Pollack, and Makoto Takeyama. A logical framework with dependently typed records. In Typed Lambda Calculus and Applications, TLCA’03, volume 2701 of Lecture Notes in Computer Science. Springer-Verlag, 2003.

[31] Judicaël Courant. A Module Calculus for Pure Type Systems. In Typed Lambda Calculi and Applications 97, volume 1210 of Lecture Notes in Computer Science, pages 112–128. Springer-Verlag, 1997.

[32] Judicaël Courant. Un calcul de modules pour les systèmes de types purs. Thèse de doctorat, Ecole Normale Supérieure de Lyon, 1998. http://www.lri.fr/~jcourant/

publis/liste_publis.en.html.

[33] Judicaël Courant. MC_{2}: A Module Calculus for Pure Type Systems. Research Report 1292, LRI, September 2001.

[34] Pierre Courtieu. Normalized Types. In Laurent Fribourg, editor, Proceedings of the Annual Conference of the European Association for Computer Science Logic, volume 2142 of Lecture Notes in Computer Science, pages 554–569, Paris, France, September 2001. Springer-Verlag.

[35] Pierre Courtieu. Représentation d’algèbres non libres en théorie des types. PhD thesis, Université Paris-Sud, 2001.

[36] Karl Crary, Robert Harper, and Sidd Puri. What is a recursive module? In SIGPLAN Conference on Programming Language Design and Implementation, pages 50–63, 1999.

[37] Haskell B. Curry and Robert Feys. Combinatory Logic I. North-Holland, Amsterdam, 1958.

[38] N.G. de Bruijn. A survey of the project Automath. In To H.B. Curry: Essays on Combinatory Logic, Lambda-Calculus and Formalism. Academic Press, 1980.

[39] Nachum Dershowitz. When are two rewrite systems more than none? Lecture Notes in Computer Science, 1295:37–??, 1997.

[40] Derek Dreyer, Karl Crary, and Robert Harper. Toward a practical type theory for recursive modules. Technical Report CMU-CS-01-112, Carnegie Mellon University, 2001.

[41] Francisco Durán and José Meseguer. An extensible module algebra for Maude. In Second International Workshop on Rewriting Logic and its Applications (WRLA’98), volume 15 of Electronic Notes in Theoretical Computer Science. Elsevier, 1998.

[42] Jean-Christophe Filliâtre. Design of a proof assistant: Coq version 7. Research Report 1369, LRI, Université Paris Sud, October 2000.

[43] Jean-Christophe Filliâtre and Pierre Letouzey. Formalization of a finite sets library

in Coq, Jul 2003. http://www.lri.fr/~filliatr/fsets/index.en.html.

[44] Herman Geuvers and Mark-Jan Nederhof. A simple modular proof of the strong normalization for the calculus of constructions. Journal of Functional Programming, 1(2):155–189, 1991.

[45] Jean-Yves Girard. Interprétation fonctionelle et élimination des coupures dans l’arithmétique d’ordre supérieur. PhD thesis, Université Paris 7, 1972.

[46] Joseph Goguen and Rod Burstall. Institutions: Abstract model theory for specification and programming. Journal of the Association for Computing Machinery, 39(1):95–146, January 1992.

[47] Robert Harper, Furio Honsell, and Gordon Plotkin. A framework for defining logics. Journal of the ACM, 40(1):143–184, January 1993.

[48] Robert Harper and Mark Lillibridge. A type-theoretic approach to higher-order modules with sharing. In POPL’94 [87], pages 123–137.

[49] Robert Harper and Frank Pfenning. A module system for a programming language based on the LF logical framework. Journal of Logic and Computation, 1(8), 1998.

[50] Tom Hirschowitz and Xavier Leroy. Mixin modules in a call-by-value setting. In Daniel Le Métayer, editor, ESOP, volume 2305 of Lecture Notes in Computer Science, pages 6–20, April 2002.

[51] Tom Hirschowitz, Xavier Leroy, and Joe B. Wells. On the implementation of recursion in call-by-value functional languages. Research report 4728, INRIA, 2003.

[52] The ISABELLE system. http://isabelle.in.tum.de/.

[53] Jean-Pierre Jouannaud and Mitsuhiro Okada. Executable higher-order algebraic specification languages. In Proceedings of the Sixth Annual IEEE Symposium on Logic in Computer Science, pages 350–361, Amsterdam, The Netherlands, July 1991. IEEE Comp. Soc. Press.

[54] Jean-Pierre Jouannaud and Mitsuhiro Okada. Abstract data type systems. Theoretical Computer Science, 173(2):349–391, February 1997.

[55] Jean-Pierre Jouannaud and Albert Rubio. The higher-order recursive path ordering. In Giuseppe Longo, editor, Fourteenth Annual IEEE Symposium on Logic in Computer Science, Trento, Italy, July 1999. IEEE Comp. Soc. Press.

[56] Florian Kammüller. Modular reasoning in Isabelle. In 17th International Conference on Automated Deduction, CADE-17, volume 1831 of Lecture Notes in Artificial Intelligence. Springer-Verlag, 2000.

[57] Florian Kammüller and Lawrence C. Paulson. A formal proof of Sylow’s first theorem – an experiment in abstract algebra with Isabelle HOL. Journal of Automated Reasoning, 23(3–4), Nov 1999.

[58] Emmanuel Kounalis. Completeness in data type specifications. In Proceedings of EUROCAL 85, volume 204 of Lecture Notes in Computer Science, pages 348–362. Springer-Verlag, April 1985.

[59] Xavier Leroy. Manifest types, modules, and separate compilation. In POPL’94 [87], pages 109–122.

[60] Xavier Leroy. Applicative functors and fully transparent higher-order modules. In Conference Record of the 22nd Symposium on Principles of Programming Languages, pages 142–153, San Francisco, California, January 1995. ACM Press.

[61] Xavier Leroy. A syntactic theory of type generativity and sharing. Journal of Functional Programming, 6(5):667–698, 1996.

[62] Xavier Leroy. A modular module system. Journal of Functional Programming, 10(3):269–303, 2000.

[63] Mark Lillibridge. Translucent Sums: A Foundation for Higher-Order Module Systems. PhD thesis, School of Computer Science, Carnegie Mellon University, May 1997.

[64] Zhaohui Luo. An Extended Calculus of Constructions. PhD thesis, University of Edinburgh, 1990.

[65] David B. MacQueen. Modules for Standard ML. In Proceedings of the 1984 ACM Conference on LISP and Functional Programming, pages 198–207. ACM Press, 1984.

[66] David B. MacQueen. Using dependent types to express modular structures. In Conference Record of the 13th Annual ACM Symposium on Principles of Programming Languages, pages 277–286, St. Petersburg, FL, January 1986.

[67] David B. MacQueen and Mads Tofte. Semantics for higher order functors. In European Symposium on Programming, volume 788 of Lecture Notes in Computer Science, pages 409–423, April 1994.

[68] Claude Marché, Christine Paulin, and Xavier Urbain. The Krakatoa tool for JML/Java program certification. Available at http://krakatoa.lri.fr, 2003.

[69] The Maude system. http://maude.cs.uiuc.edu/.

[70] Robin Milner, Mads Tofte, Robert Harper, and David MacQueen. The Definition of Standard ML (Revised). MIT Press, 1997.

[71] John Mitchell, Sigurd Meldal, and Neel Madhav. An extension of Standard ML modules with subtyping and inheritance. In Conference Record of the Eighteenth Annual ACM Symposium on Principles of Programming Languages, pages 270–278. ACM SIGACT and SIGPLAN, ACM Press, 1991.

[72] John C. Mitchell and Gordon D. Plotkin. Abstract types have existential type. ACM Transactions on Programming Languages and Systems, 10(3):470–502, July 1988.

[73] Moscow ML. http://www.dina.kvl.dk/~sestoft/mosml.html.

[74] Fritz Müller. Confluence of the lambda calculus with left-linear algebraic rewriting. Information Processing Letters, 41(6):293–299, 1992.

[75] Tobias Nipkow and Christian Prehofer. Type reconstruction for type classes. Journal of Functional Programming, 5(2):201–224, 1995.

[76] Objective Caml. http://caml.inria.fr/.

[77] Enno Ohlebusch. On the modularity of termination of term rewriting systems. Theoretical Computer Science, 136:333–360, 1994.

[78] S. Owre, N. Shankar, J. M. Rushby, and D. W. J. Stringer-Calvert. PVS Language Reference. Computer Science Laboratory, SRI International, Menlo Park, CA, September 1999.

[79] Sam Owre and Natarajan Shankar. The formal semantics of PVS. Technical Report SRI-CSL-97-2, Computer Science Laboratory, SRI International, Menlo Park, CA, August 1997.

[80] Christine Paulin-Mohring. Inductive definitions in the system Coq: Rules and properties. In M. Bezem and J. F. Groote, editors, Proceedings of the 1st International Conference on Typed Lambda Calculi and Applications, volume 664 of Lecture Notes in Computer Science, pages 328–345, Utrecht, The Netherlands, March 1993. Springer-Verlag.

[81] Christine Paulin-Mohring. Définitions inductives en théorie des types d’ordre supérieur. Thèse d’habilitation, Ecole Normale Supérieure de Lyon, 1996.

[82] Lawrence C. Paulson. Isabelle: the next 700 theorem provers. In P. Odifreddi, editor, Logic and Computer Science. Academic Press, 1990.

[83] Frank Pfenning. Elf: A language for logic definition and verified meta-programming. In Proc. 4th IEEE Symp. Logic in Computer Science, pages 313–322, 1989.

[84] Frank Pfenning. Logic programming in the LF logical framework. In Gérard Huet and Gordon D. Plotkin, editors, Logical Frameworks. Cambridge University Press, 1991.

[85] Frank Pfenning and Christine Paulin-Mohring. Inductively defined types in the Calculus of Constructions. In Proceedings of Mathematical Foundations of Programming Semantics, volume 442 of Lecture Notes in Computer Science. Springer-Verlag, 1990.

[86] Robert Pollack. Dependently typed records in type theory. Formal Aspects of Computing, 13:386–402, 2002.

[87] Conference Record of the 21st Symposium on Principles of Programming Languages, Portland, Oregon, 1994. ACM Press.

[88] The PVS specification and verification system. http://pvs.csl.sri.com/.

[89] Claudio V. Russo. Types For Modules. LFCS thesis ECS-LFCS-98-389, University of Edinburgh, 1998.

[90] Claudio V. Russo. First-class structures for Standard ML. Nordic Journal of Computing, 7(4):348–374, 2000.

[91] Claudio V. Russo. Recursive structures for Standard ML. In Proceedings of the Sixth ACM SIGPLAN International Conference on Functional Programming, pages 50–61, Florence, Italy, September 2001.

[92] Donald Sannella and Andrzej Tarlecki. Essential concepts of algebraic specification and program development. Formal Aspects of Computing, 9:229–269, 1997.

[93] Paula Severi and Erik Poll. Pure type systems with definitions. In Logical Foundations of Computer Science’94, volume 813 of Lecture Notes in Computer Science, pages 316–328. Springer-Verlag, 1994.

[94] Milena Stefanova. Properties of Typing Systems. PhD thesis, University of Nijmegen, 1999.

[95] William W. Tait. Infinitely long terms of transfinite type. In J.N. Dummett M.A.E. Crossley, editor, Formal Systems and Recursive Functions, Studies in Logic and the Foundations of Mathematics, pages 176–185. North-Holland, 1965.

[96] Jan Terlouw. Een nadere bewijstheoretische analyse van GSTTs. Manuscript, University of Nijmegen, Netherlands, 1989.

[97] Diedrik T. van Daalen. The language theory of AUTOMATH. PhD thesis, Technical University Eindhoven, Eindhoven, The Netherlands, 1980.

[98] Daria Walukiewicz-Chrząszcz. Termination of rewriting in the calculus of constructions. Journal of Functional Programming, 13(2):339–414, 2003.

[99] Daria Walukiewicz-Chrząszcz. Termination of Rewriting in the Calculus of Constructions. PhD thesis, Warsaw University and University Paris XI, 2003.

[100] Benjamin Werner. Méta-théorie du Calcul des Constructions Inductives. PhD thesis, Université Paris 7, 1994.