TYPE-THEORETIC METHODOLOGY FOR PRACTICAL PROGRAMMING LANGUAGES

A Dissertation

Presented to the Faculty of the Graduate School

of Cornell University

in Partial Fulfillment of the Requirements for the Degree of Doctor of Philosophy

by Karl Fredrick Crary

TYPE-THEORETIC METHODOLOGY FOR PRACTICAL PROGRAMMING LANGUAGES

August 1998 \begin{matrix}c \\ ◯ \end{matrix} Karl Fredrick Crary 1998 ALL RIGHTS RESERVED Karl Fredrick Crary, Ph.D. Cornell University 1998

The significance of type theory to the theory of programming languages has long been recognized. Advances in programming languages have often derived from understanding that stems from type theory. However, these applications of type theory to practical programming languages have been indirect; the differences between practical languages and type theory have prevented direct connections between the two.

This dissertation presents systematic techniques directly relating practical programming languages to type theory. These techniques allow programming languages to be interpreted in the rich mathematical domain of type theory. Such interpretations lead to semantics that are at once denotational and operational, combining the advantages of each, and they also lay the foundation for formal verification of computer programs in type theory.

Previous type theories either have not provided adequate expressiveness to interpret practical languages, or have provided such expressiveness at the expense of essential features of the type theory. In particular, no previous type theory has supported a notion of partial functions (needed to interpret recursion in practical languages), and a notion of total functions and objects (needed to reason about data values), and an intrinsic notion of equality (needed for most interesting results). This dissertation presents the first type theory incorporating all three, and discusses issues arising in the design of that type theory.

This type theory is used as the target of a type-theoretic semantics for a expressive programming calculus. This calculus may serve as an internal language for a variety of functional programming languages. The semantics is stated as a syntax-directed embedding of the programming calculus into type theory.

A critical point arising in both the type theory and the type-theoretic semantics is the issue of admissibility. Admissibility governs what types it is legal to form recursive functions over. To build a useful type theory for partial functions it is necessary to have a wide class of admissible types. In particular, it is necessary for all the types arising in the type-theoretic semantics to be admissible. In this dissertation I present a class of admissible types that is considerably wider than any previously known class.

Biographical Sketch

Karl Crary was born in Madison, Wisconsin in 1971. In 1977 he moved with his family to Seattle, Washington where he grew up. He started college at Carnegie Mellon University in 1989, and earned bachelor’s degrees in Computer Science and Economics in 1993. He then began graduate school at Cornell University where he remained until 1998.

Acknowledgements

I wish to thank my parents, Fred and Betsy, for their constant love and support throughout my childhood and adulthood. More than anyone else, they have shaped the way I see and think about the world. I want to thank Ross Willard for introducing me to logic and modern mathematics, and John Reynolds for introducing me to the world of type theory. I also am very grateful to Robert Harper for serving as my undergraduate thesis advisor, for teaching me so much, and for all his encouragement while I was his student and continuing even today.

Particular thanks go to Robert Constable, my graduate advisor, and to Greg Morrisett. The least they did was to teach me most of the material on which this dissertation, and the rest of my work, is based. More importantly they were a constant source of ideas, sound advice and encouragement. I also wish to thanks Dexter Kozen for all his help and influence.

I also wish to thank all the many teachers, family and friends who helped me get where I am today, and who made it pleasant getting here. Finally, I want to thank God for creating the world, and for showing me the life beyond.

Table of Contents

1 Introduction 1

1.1 The KML Programming Language . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.1.1 Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.1.2 Comparison . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.1.3 The Role of Type Theory in the Design . . . . . . . . . . . . . . . . . . . 3

1.2 Principals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.2.1 Type Theory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.2.2 Lambda-K . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

1.3 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2 Lambda-K 6 2.1 The Core Calculus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2 The Module Calculus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.3 Static Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.4 Design Issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.4.1 The Phase Distinction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.4.2 Top and Bottom Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3 Type-Theoretic Semantics 13 3.1 A Computational Denotational Semantics . . . . . . . . . . . . . . . . . . . . . . 14 3.2 The Language of Type Theory . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.2.1 Domain and Category Theory . . . . . . . . . . . . . . . . . . . . . . . . . 15 3.3 A Type-Theoretic Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

3.3.1 Basic Embedding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.3.2 Embedding Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.3.3 Embedding Kinds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.3.4 Embedding Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

3.4 Properties of the Embedding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 3.5 Prospects for Extension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 3.6 Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

4 Foundational Type Theory 32

4.1 Nuprl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 4.1.1 Basic Types and Operators . . . . . . . . . . . . . . . . . . . . . . . . . . 32 4.1.2 Equality and Well-formedness . . . . . . . . . . . . . . . . . . . . . . . . . 34 4.1.3 Propositions as Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 4.1.4 Equality, Subtyping and Inequality . . . . . . . . . . . . . . . . . . . . . . 35 4.1.5 Constructivity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 4.1.6 Constructing New Types using Logic . . . . . . . . . . . . . . . . . . . . . 37 4.1.7 Computation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 4.1.8 Partial Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

4.2 Sequents and Proof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 4.2.1 Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 4.2.2 Extract-Style Proof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

4.3 Issues in Partial Object Type Theory . . . . . . . . . . . . . . . . . . . . . . . . . 45 4.3.1 Partial Computation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 4.3.2 Computation in Active Positions . . . . . . . . . . . . . . . . . . . . . . . 46 4.3.3 Fixpoint Induction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 4.3.4 Computational Induction . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 4.3.5 Other Contributions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

4.4 Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 4.4.1 Type Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 4.4.2 Judgement Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 4.4.3 Classical Reasoning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

5 Admissibility 59 5.1 The Fixpoint Principle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 5.2 Computational Lemmas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

5.2.1 Computational Approximation . . . . . . . . . . . . . . . . . . . . . . . . 61 5.2.2 Finite Approximations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 5.2.3 Least Upper Bound Theorem . . . . . . . . . . . . . . . . . . . . . . . . . 62

5.3 Admissibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 5.3.1 Predicate-Admissibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 5.3.2 Monotonicity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 5.3.3 Set and Quotient Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 5.3.4 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

5.4 Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

6 Conclusion 72

A Lambda-K Typing Rules 74

B Nuprl Proof Rules 79

C Proofs 89

Bibliography 98

List of Figures

2.1 Lambda-K Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2 Lambda-K Shorthand . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.3 Higher Order Power and Singleton Kinds . . . . . . . . . . . . . . . . . . . . . . 8 2.4 A Typical Module Encoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

3.1 Type Theory Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.2 The Type Triangle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 3.3 Embedding Kinds and Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 3.4 Embedding Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 3.5 Embedding Signatures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 3.6 Embedding Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

4.1 Basic Nuprl Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 4.2 Derived Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 4.3 Operational Semantics for Nuprl . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 4.4 Primitive Nuprl Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 4.5 Type Specifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 4.6 Type Definitions (Data Types) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 4.7 Type Definitions (Assertions) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 4.8 Type Definitions (Set and Quotient) . . . . . . . . . . . . . . . . . . . . . . . . . 55

5.1 Admissibility, coadmissibility and monotonicity conditions . . . . . . . . . . . . . 71

Chapter 1

Introduction

The distance between theory and practice is shorter in theory than in practice. —Unknown

The development of practical programming languages has lagged far behind the state-of-the-art of theoretical programming language study. Examples abound of programming language techniques and methods that have been well known and understood for years, but still have yet to appear in any practical programming system. This can be frustrating to the programming language theoretician who also writes code, and that frustration is not diminished by the knowledge that the gap between theory and practice is common to many, if not most, fields of study.

The main reason that the gap between theory and practice is often so wide is the issue of elegance. Programming languages can admit extremely elegant theoretical study. This elegance is partly responsible for the rapid progress of results in theoretical programming languages, and it is partly responsible for the attractiveness of programming languages as a field of study. However, such elegance is usually achieved by abstracting away from the details of real languages—details that are essential for real languages to be usable.

In my thesis research, I set out to design and implement a practical programming language to incorporate unutilized developments in programming language theory. The result was a dialect of ML that came to be called KML. Not surprisingly, I was soon grappling with the elegance gap between theory and practice. To manage the complexity of a real system, I allowed type theory to drive the design. That is, I adopted the strategy of choosing, at every design point, an option that was easily represented and justified in an underlying foundation of type theory.

This strategy proved remarkably effective, and resulted in a language design that was versatile and powerful but also relatively simple. However, allowing type theory to drive the design of KML paid another dividend, which had wider applications than to the KML language alone. The type-theoretic design of KML led me to explore not only how type theory can motivate language design informally, but also to explore how (and why) to draw formal connections between practical programming languages and foundational type theory.

This dissertation focuses on the latter aspect of my thesis research, formal connections between practical programming languages and foundational type theory. In this thesis we will see various specific applications of such connections, but from a broader standpoint the aim of the work I discuss here is to draw theory and practice closer together. The constructs that make up type theory are powerful and uniform, making it possible to reason about issues facing real languages without sacrificing elegance.

1.1 The KML Programming Language

A complete discussion of the KML programming language is beyond the scope of this dissertation. I give here a brief and informal introduction to KML and its contributions.

1.1.1 Features

The main objective of KML was first to incorporate into a practical dialect of the ML programming language a number of useful language features not present in preceding dialects of ML while building the entire language on a solid theoretical foundation. KML is structured to greatly resemble Standard ML; for the most part, Standard ML is a strict subset of KML. The differences between KML and Standard ML lie in the way KML is specified, and in the additional language features supported by KML: object-oriented programming, subtyping, a more expressive module system and first-class polymorphism. These features are chosen because they can assist in a real and practical way with the task of programming.

A KML compiler is implemented that consists of a KML-specific front end, which compiles KML to an intermediate language based on the polymorphic lambda calculus, and a back end that compiles that intermediate language to Typed Assembly Language [77, 76].

At the time of this writing, the design of KML is substantially complete but is still changing in a number of areas. Therefore, the reader should take the following descriptions of KML’s features as tentative.

Objects Object-oriented programming is supported in KML through a primitive object calculus [31] that is substantially similar to that of Abadi and Cardelli [1, 2]. Objects are seen as collections of method functions, each of which takes the entire object as an argument and computes a result [58]. A method’s results may be a new object of the same type; such results are usually produced by updating one of the fields of the object with a new method implementation.

In the present design, primitive objects are the extent of the support for object-oriented programming. From primitive objects it is possible to code more complicated artifices like classes and inheritance. I plan built-in syntactic support for classes and inheritance in a future version of the design.

Subtyping The KML type system completely integrates support for subtyping. Subtyping arises by dropping methods from objects and fields from records, and also from stated definitions. Definitional subtyping happens in bounded quantification, where code is written to be polymorphic over all types that are subtypes of a given type, and in abstract subtyping, where a type in a module is specified to be a subtype of some other type. KML allows unrestricted subsumption, that is, a member of a subtype may always be used in place of a member of the supertype without any mediating syntax such as a coercion.

Expressive Modules The KML module system builds on the module system of Standard ML with two main enhancements. The first is higher-order functors. While Standard ML functors may take and return only structures, the KML module system allows functors to take other functors as arguments and return functors as results. Thus, the KML module system is fully featured typed lambda calculus.

The second enhancement is to allow type definitions in signatures. Allowing type definitions in signatures, combined with a type system that leaks no type information in the absence of such definitions, allows so-called “translucent” modules, modules that leak exactly the desired amount of type information. Translucent modules allow the greatest amount of abstraction possible in a system, while still permitting enough leakage to link modules together.

First-Class Polymorphism Most dialects of ML, including Standard ML, restrict polymorphic terms so that they cannot be placed into data structures, passed to functions or returned by functions. (Formally, this restriction stems from a prenex restriction on quantified types: quantifiers must appear all the way out, which means that quantified types cannot make up a component of any larger type.) Thus, polymorphic values are not first class; they must always be instantiated before they may be used. In contrast, polymorphic values in KML are first class; they may be arguments or results of functions and may be stored in data structures.

KML also permits polymorphic recursion [78], where a polymorphic function calls itself recursively with a different type argument. Polymorphic recursion is necessary to make good use of first-class polymorphism (otherwise all type arguments are statically predetermined), but it is also useful in other contexts [79].

1.1.2 Comparison

During the time I was developing KML, Xavier Leroy and his colleagues released the Objective CAML system [64]. Objective CAML shares many of the same goals as KML, in particular the goals of providing support for object-oriented programming and a more expressive module system. However, Objective CAML does not support first-class polymorphism and possesses no formal semantics or definition.

The greatest similarity between KML and Objective CAML lies in the module systems, which provide almost identical expressiveness, despite various technical differences. A greater contrast exists in the object system. The object system of KML is derived from the object calculus of Crary [31], which derives its lineage from the object systems of Abadi and Cardelli [1, 2] and their predecessors. In contrast, the object system of Objective CAML is based on the object system of Rémy and Vouillon [85], which derives its lineage from the record extension calculi of Rémy [84].

The greatest practical difference between the object systems of KML and Objective CAML lies in support for subtyping. Unlike KML, Objective CAML provides no automatic subsumption; objects must be coerced to supertypes by an explicit coercion. However, by abandoning subtyping with subsumption, Objective CAML is able to enjoy feasible type inference, which is prohibitively difficult for KML. Moreover, in many cases the practical benefits of subtyping are obtained in Objective CAML by quantification over row variables that extend object types to unspecified supertypes (details appear in Rémy and Vouillon [85] and Leroy [64]).

1.1.3 The Role of Type Theory in the Design

In order to produce an elegant and coherent design for KML, I allowed type-theoretic considerations to drive the design. In order to convey the flavor of this interaction, I briefly describe two design points that were informed by type theory.

In a module calculus, one important issue to settle is the equational theory: under what circumstances are modules and components of modules considered equal to others. The definition of Standard ML [71] employed a system of unique stamps to dictate when two modules were considered equal. This system becomes even more involved when extended to higher-order modules [66]. In KML, I simplified the equational theory by eliminating equality of modules in favor of equality of the types that make up those modules (a similar choice was made for Standard ML (Revised) [72]), and then ruling that types are equal exactly when equivalence may be proven from stated type definitions in a logic of types. (Leroy made a similar decision in the design of the Objective CAML module system.)

Another key design point was what form support for object-oriented programming should take. Rather than begin with a source level system of objects or classes, I focused on the typetheoretic interpretation of objects. With an theory of objects in hand, I worked backwards to the KML object calculus. The result was an object system with comparable power to that of Abadi and Cardelli [1, 2], but with a simpler theory and a more efficient implementation. Details appear in Crary [31].

1.2 Principals

1.2.1 Type Theory

One may view foundational type theory as being, at its core, a typed functional programming language with a very rich type system. That type system is capable of expressing complex relationships between objects; sufficiently complex, that it may encode (almost) any sentence in logic and mathematics. In this way, type theory may serve as a foundation for mathematics and computation.

To some degree, this view reflects the bias of a computer scientist. Type theory has been used as a foundation for mathematics since before the advent of modern computers; for instance, Whitehead and Russell’s influential 1910 treatise Principia Mathematica [96] was a type theory in some ways similar to the type theory of this dissertation. Nevertheless, type theory has been particularly successful as a foundation for computer science. This is largely because type theory deals elegantly with structured data and computation (the “stuff” of computer science), which must be painstakingly constructed in most other foundational frameworks.

The particular type theory I use in this dissertation is a variant of the Nuprl type theory [19]. Nuprl traces its lineage back to Martin-Löf’s Intuitionistic Type Theory [67], which was first intended to clarify the syntax and semantics of intuitionistic mathematics. The Nuprl type theory adapted Intuitionistic Type Theory as a foundation for computer programming by simplifying its structure and adding a number of new type constructors. Although my results are stated in the context of the Nuprl type theory, my intention is that the methodology behind those results be applicable to type theories in general.

1.2.2 Lambda-K

In order to manage the complexities of a real programming language, KML is defined in terms of a smaller internal language called λ^{K}. Lambda-K provides all the expressiveness of the full KML language, but is considerably simpler and more elegant, because many of the complex constructs of KML are broken up by the definition into simpler constructs in λ^{K}.

The main thrust of this dissertation, then, is an exploration of how to draw formal connections between the internal language λ^{K} and the Nuprl type theory. These formal connections then apply back to the high-level language KML by way of its definition. However, I wish to view the KML particulars as only a case study in the general methodology I explore. Similar results can be drawn for other languages; for example, Harper and Stone [48] give an interpretation of Standard ML in terms of an internal language very much like λ^{K}. Thus, with some minor modifications, my results would apply to Standard ML as well. Accordingly, I do not present the formal definition of KML in λ^{K} in this dissertation; to do so would unduly focus on the particulars and distract from the broader methodology.

The Nuprl type theory is predicative, so in order to draw formal connections to Nuprl from λ^{K}, it is necessary for λ^{K} to be predicative as well. However, the λ^{K} used in defining KML is not predicative. Consequently, in this dissertation I use a predicative variant of λ^{K} that drops a number of features. Recursive types, stateful computation and objects all depend on impredicativity in some way, and are regrettably omitted. Restoring these is an important avenue for future research, and I discuss some possible ways it might be done in Section 3.5.

1.3 Overview

After this introductory chapter, the technical material commences in Chapter 2 with an exposition KML’s internal language λ^{K}. In keeping with the view of λ^{K} as a case study, that chapter is brief. The primary original contribution of λ^{K} is a module calculus that combines contributions from the translucent sums calculi of Harper and Lillibridge [44] and Leroy [62], the phase-distinction calculus of Harper, Mitchell and Moggi [46], and the applicative functors calculus of Leroy [63].

Chapter 3 begins with an informal description of the Nuprl type theory, and then continues with an embedding of λ^{K} into Nuprl, the first of the three main technical contributions of this dissertation. That embedding provides a type-theoretic semantics of λ^{K}, and thereby draws a sharp formal connection between KML and type theory. I also discuss at length the applications and some of the consequences of the semantics.

In Chapter 4 I discuss in detail my variant of the Nuprl type theory, the second main technical contribution of this dissertation. This type theory is the first constructive type theory to include an intrinsic notion of equality and also to allow reasoning about both partial and total functions. All three notions are necessary to give an adequate account of practical programming languages; partiality is clearly necessary for Turing-complete languages, and equality and totality are necessary to say anything interesting about such languages once interpreted. I also discuss practical issues that arise when using a type theory incorporating partiality. Finally, I give a rigorous semantics to the type theory and show its consistency.

Central to the type theory is a fixpoint rule for typing recursive functions. However, the fixpoint rule is not valid for recursive functions on every type. Types for which the fixpoint rule is valid are known as admissible. In Chapter 5 I give general techniques for showing types to be admissible, the third main technical contribution of this dissertation. Before this work, type theories incorporating partiality were handicapped by a dearth of types known to be admissible. A critical step in the method is a elegant least upper bound theorem for computational approximation with applications beyond the admissibility results to which I apply it.

Concluding remarks appear in Chapter 6. Following my concluding remarks, the dissertation closes with three appendices. Appendix A gives the complete typing rules for λ^{K}. Appendix B gives the complete inference rules for Nuprl proofs. In the interest of readability throughout the dissertation, difficult proofs are removed to Appendix C.

Lambda-K

The KML programming language provides a very expressive language for practical programming. However, experience with Standard ML has shown that although direct formal definitions of practical programming languages may be written [71, 72], those definitions are too unwieldy to be very useful except as a language specification for compiler implementers and are particularly impractical for theoretical study.^{1} Simple languages are much more practical for theoretical study, and are useful in implementations as well: The first step of a compiler is typically to elaborate the external language into a simpler intermediate language.

In order to use a simpler and more manageable language than full KML, in this thesis I will be using a simple typed calculus called λ^{K}. A framework that proved to be workable was to augment the higher-order polymorphic lambda calculus, F_{ω} [35, 36], with power [17, 18] and singleton [41] kinds, dependent function and record kinds, and strong sums. This design is discussed at length in this chapter.

The syntax of λ^{K} is defined by the rules given in Figure 2.1 and consists of five syntactic classes. The class of terms contains the basic constructs of the polymorphic lambda calculus with records. The class of type constructors (which I will usually refer to as “constructors”) contains the types of well-formed terms and a lambda calculus (with records) built over them for computing types. (Typical type constructors are actual types, such as integers, or (first-order) type operators, such as lists.) The class of kinds then contains the “types” of type constructors. The two remaining classes, modules and signatures, contain the terms and types of λ^{K}’s module system. The static semantics of λ^{K} also uses contexts, which assign kinds, types and signatures to constructor variables (ranged over by α), term variables (ranged over by x) and module variables (ranged over by s). Figure 2.2 contains several convenient abbreviations for use with λ^{K}.

Throughout this thesis, in any calculi with binding structure (such as λ^{K}), I consider two expressions to be identical if they differ only by alpha-variation. When two expressions e_{1} and e_{2} are alpha-equal I write e_{1} ≡ e_{2}. Also, the simultaneous capture-avoiding substitution of expressions e_{1}, …, e_{n} for variables x_{1}, …, x_{n} in e will be denoted by e[e_{1} ⋯ e_{n}/x_{1} ⋯ x_{n}].

1The Definition of Standard ML [71] runs to 65 pages and consists of 196 rules (plus many additional implied rules for propagating exception packets) and considerable supporting machinery. The revised Definition [72] is simpler, but still runs to 54 pages and 189 rules.

kinds  κ ::= Type_{i} | Πα:κ_{1}.κ_{2} | {ℓ_{1} ▷ α_{1} : κ_{1}, …, ℓ_{n} ▷ α_{n} : κ_{n}} | P_{i}(c) | S_{i}(c)
constructors  c ::= α | λα:κ.c | c_{1}[c_{2}] | {ℓ_{1} = c_{1}, …, ℓ_{n} = c_{n}} | π_{ℓ}(c) | c_{1} → c_{2} | c_{1} ⇒ c_{2} |

∀α:κ.c | {ℓ_{1} : c_{1}, …, ℓ_{n} : c_{n}} | ⟨ℓ_{1} : c_{1}, …, ℓ_{n} : c_{n}⟩ | ext(m)

terms  e ::= x | λx:c.e | e_{1}e_{2} | Λα:κ.e | e[c] | {ℓ_{1} = e_{1}, …, ℓ_{n} = e_{n}} | π_{ℓ}(e) | inj_{ℓ}(e) |

case(e, ℓ_{1} ▷ x_{1}. e_{1}, …, ℓ_{n} ▷ x_{n}. e_{n}) | fix_{c}(e) | ext(m)

signatures  σ ::= ⟨κ⟩ | ⟨⟨c⟩⟩ | Πs:σ_{1}.σ_{2} | {ℓ_{1} ▷ s_{1} : σ_{1}, …, ℓ_{n} ▷ s_{n} : σ_{n}}
modules  m ::= s | ⟨c⟩ | ⟨⟨e⟩⟩ | λs:σ.m | m_{1}m_{2} | {ℓ_{1} = m_{1}, …, ℓ_{n} = m_{n}} | π_{ℓ}(m) | m : σ
contexts  Γ ::= • | Γ[α : κ] | Γ[x : c] | Γ[s : σ]

Figure 2.1: Lambda-K Syntax

τ_{1} × ⋯ × τ_{n} =^{def} {l1 : τ_{1}, …, ln : τ_{n}}
⟨e_{1}, …, e_{n}⟩ =^{def} {l1 = e_{1}, …, ln = e_{n}}
π_{i}(e) =^{def} π_{li}(e)
τ_{1} + ⋯ + τ_{n} =^{def} ⟨l1 : τ_{1}, …, ln : τ_{n}⟩
inj_{i}(e) =^{def} inj_{li}(e)
case(e, x_{1}.e_{1}, …, x_{n}.e_{n}) =^{def} case(e, l1 ▷ x_{1}. e_{1}, …, ln ▷ x_{n}. e_{n})

(where l1, l2, … are distinguished labels)

Figure 2.2: Lambda-K Shorthand

2.1 The Core Calculus

The type system of λ^{K} includes functions and records at the term and constructor levels, and polymorphic functions at the term level, using the standard notations. Evaluation is intended to be call-by-value. Records of terms, record types, and records of constructors that differ only in field order are considered identical. The type of functions from τ_{1} to τ_{2} is denoted by τ_{1} → τ_{2}; functions that return without computational effects are considered pure functions and may be given the pure function type τ_{1} ⇒ τ_{2} (for appropriate τ_{1} and τ_{2}). The type system also includes the labelled disjoint union type, denoted by ⟨ℓ_{1} : c_{1}, …, ℓ_{n} : c_{n}⟩, members of which are formed by inj_{ℓ}(e) and eliminated by case(e, ℓ_{1} ▷ x_{1}. e_{1}, …, ℓ_{n} ▷ x_{n}. e_{n}). As with records, disjoint union types or case expressions that differ only in field order are considered identical. Recursive functions are supported in the standard way by a fix operator. The remaining construct, ext(m), deals with the module calculus and is discussed in Section 2.2.

A subtyping relation is defined over the types of λ^{K}. Intuitively, τ_{1} is a subtype of τ_{2} (denoted τ_{1} ⪯ τ_{2}) when every term belonging to τ_{1} also belongs to τ_{2}. As usual, functions are contravariant on the left and covariant on the right; records are covariant in the field types and subtypes are produced by adding fields. Pure function types are subtypes of the corresponding function types.

Kinds The kind level is more novel. Ignoring the i annotations, Type contains well-formed types. The power kind, denoted by P(τ) for any type τ, contains well-formed types that are subtypes of the given type τ. The singleton kind, denoted by S(τ) for any type τ, contains well-formed types that are equivalent to the given type τ. The level annotations i, which are positive integers, restrict the memberships of these kinds. The level of a kind or type is defined to be the highest level annotation appearing within it. The kinds Type_{i}, P_{i}(τ) and S_{i}(τ) contain

R(c : Type_{i}) =^{def} R_{i}(c)
R(c : κ_{1} → κ_{2}) =^{def} Πα:κ_{1}.R(c[α] : κ_{2})
R(c : {ℓ_{i} : κ_{i}^{[i=1...n]}}) =^{def} {ℓ_{i} : R(π_{ℓ_{i}}(c) : κ_{i})^{[i=1...n]}}

(R represents either P or S)

Figure 2.3: Higher Order Power and Singleton Kinds

only types with levels less than i. Thus, types belonging to Type_{1} cannot contain quantify over any (nontrivial) kinds, and types belonging to Type_{2} can only quantify over level-1 kinds. For P_{i}(c) or S_{i}(c) the level of c must be less than i.

This level annotation mechanism makes λ^{K} predicative, which is necessary to perform Chapter 3’s embedding into the predicative type theory of Nuprl. Since these annotations are tedious to provide, I discuss some alternatives in Section 3.5. In a practical system based on this annotation mechanism, the system would have to infer level annotations for the user [47].

The kind of functions mapping constructors of kind κ_{1} to constructors of kind κ_{2} may be denoted κ_{1} →κ_{2}, but since constructors may appear within kinds, it is desirable to allow a more precise characterization of such functions: The kind Πα:κ_{1}.κ_{2} includes all functions mapping κ_{1} constructors to κ_{2} constructors where α stands for the function’s argument and may appear free in κ_{2}. Such kinds are referred to as dependent function kinds since the result kind depends on the argument. For example, a function with kind Πα:Type_{i}.P_{i}(α) when applied to any type τ returns a constructor of kind P_{i}(τ), that is, a subtype of τ. The independent function kind κ_{1} → κ_{2} is shorthand for Πα:κ_{1}.κ_{2} when α does not appear free in κ_{2}.

Records of type constructors may also be given dependent kinds. The kind {ℓ_{1} ▷ α_{1} : κ_{1}, …, ℓ_{n} ▷ α_{n} : κ_{n}} contains all records {ℓ_{1} = c_{1}, …, ℓ_{n} = c_{n}} such that each c_{i} has the corresponding kind κ_{i} with the values of preceding fields substituted for the free variables. That is, c_{i} must have kind κ_{i}[c_{1} ⋯ c_{i−1}/α_{1} ⋯ α_{i−1}]. The external labels (ranged over by ℓ) must be kept distinct from the internal binding occurrences because variables must alpha-vary but labels must not.^{2} This is discussed in greater length in Harper and Lillibridge [44]. As a shorthand, I will often omit ▷ α (pronounced “as alpha”) from a dependent record kind when α does not appear free in the following kinds; an independent record kind is one that contains no internal binding occurrences at all. I consider two dependent record kinds to be identical when they differ only in field order, so long as no field is reordered to appear after a field that depends upon it.

Although power and singleton kinds are made using only types (not higher-order constructors), dependent kinds may be used to build higher-order power and singleton kinds. For example, the kind Πα:Type_{i}.P_{i}(list[α]) includes type constructors that are pointwise subtypes of list. Higher-order power and singleton kinds may be generally defined as in Figure 2.3.

Just as a subtyping relation was defined over the types, a subkinding relation is defined over the kinds. This relation is denoted by κ_{1} ⪯ κ_{2} when κ_{1} is a subkind of κ_{2}. Function and record kinds obey similar rules to the subtyping rules. Additional subkinding relationships result from level annotations, Type_{i} ⪯ Type_{i+1} (and likewise for singleton and power kinds), and from power and singleton kinds: for any type τ, S_{i}(τ) ⪯ P_{i}(τ) ⪯ Type_{i}, and also P_{i}(τ_{1}) ⪯ P_{i}(τ_{2}) when τ_{1} ⪯ τ_{2}.

2These two names may be burdensome for a programmer to supply, so, as a practical matter, in KML it is permitted to write one name for use as both the internal and the external name; this poses no problems so long as it is possible to use separate names when they are needed [44].

 sig
    tycon foo : type
    module S1 :
       sig
          tycon bar : type
          tycon baz = foo -> bar
          val gnurf : baz
       end
    val blap : S1.bar
 end

⟹

{ foo ▷ s_{foo} : ⟨Type⟩,
S1 ▷ s_{S1} :
{ bar ▷ s_{bar} : ⟨Type⟩,
baz ▷ s_{baz} : ⟨S(ext(s_{foo}) → ext(s_{bar}))⟩,
gnurf : ⟨⟨ext(s_{baz})⟩⟩ },
blap : ⟨⟨ext(π_{bar}(s_{S1}))⟩⟩ }

 struct
    tycon foo = int -> int
    module S1 =
       struct
          tycon bar = int
          tycon baz =
             (int -> int) -> int
          val gnurf =
             fn f : int -> int => f 0
       end
    val blap = 12
 end

⟹ { foo = ⟨int → int⟩,

                                                 S1 =

{ bar = ⟨int⟩, baz = ⟨(int → int) → int⟩, gnurf = ⟨⟨λf:(int → int). f 0⟩⟩ },

blap = ⟨⟨12⟩⟩ }

Figure 2.4: A Typical Module Encoding

2.2 The Module Calculus

Lambda-K modules form their own syntactic class, and their types are called signatures, which also form their own syntactic class. A primitive module is either a single constructor ⟨c⟩ or a single term ⟨⟨e⟩⟩. If c has kind κ, then ⟨c⟩ has signature ⟨κ⟩. Likewise, if e has type τ, then ⟨⟨e⟩⟩ has signature ⟨⟨τ⟩⟩.

Modules are also formed using lambda-abstractions (for functors) and records (for structures). Such modules may be given dependent function signatures and dependent record signatures that are precisely analogous to the dependent function and dependent record kind discussed above. Data abstraction is achieved by forgetting type information using the construct m : σ, which coerces the module m to have the signature σ (if that signature is valid for m); any type information about m not reflected in σ is forgotten. As with the subtyping and subkinding relations over the types and kinds, the signatures have a subsignature relation, which obeys rules similar to the rules governing subtyping and subkinding.

Modules are used within the core calculus by means of the extraction construct: ext(m) for module m. If m has signature ⟨κ⟩, then the constructor ext(m) has kind κ. Likewise, if m has signature ⟨⟨τ⟩⟩ then the term ext(m) has type τ. Exactly what modules are legal to extract from is an issue that is discussed in Section 2.4.1.

With the above constructs, it is easy to encode high-level KML modules. A KML structure is constructed by building a record out of its contents with every constructor field encased in ⟨·⟩ and every term field encased in ⟨⟨·⟩⟩. Figure 2.4 shows the encoding of a typical KML module with a substructure into λ^{K}. Note the use of an internal and an external name in the encoding of the blap field of the signature. Functors and their signatures are encoded using λ and Π in the obvious manner.

Since modules form their own syntactic class and are not reflected directly into the core calculus, λ^{K} modules are second-class. This decision is based on issues related to the phase distinction (Section 2.4.1). To permit first-class modules would require considerably different approaches to these issues that would reduce the expressiveness of the calculus, as we will see shortly. Fortunately, the first-class module restriction does not seem to be an onerous one: the most commonly cited use for a first-class module, the ability to select at run-time an efficient implementation, can be achieved using existential types [73] or objects.

2.3 Static Semantics

The static semantics of λ^{K} appears in Appendix A. Nine judgements are used; six for the core calculus and three for the module calculus:

1. The kind equality judgement Γ ⊢_{K} κ_{1} = κ_{2} indicates that κ_{1} and κ_{2} are equal (in context Γ).

2. The subkinding judgement Γ ⊢_{K} κ_{1} ⪯ κ_{2} indicates that every constructor in κ_{1} is in κ_{2}, as discussed previously.

3. The constructor equality judgement Γ ⊢_{K} c_{1} = c_{2} : κ indicates that c_{1} and c_{2} are equal as members of kind κ.

4. The subtyping judgement Γ ⊢_{K} c_{1} ⪯ c_{2} indicates that every term in c_{1} is in c_{2}.

5. The typing judgement Γ ⊢_{K} e : c indicates that the term e has the type c.

6. The valuability judgement Γ ⊢_{K} e ↓ c indicates that the term e has the type c and that its computation terminates.

7. The subsignature judgement Γ ⊢_{K} σ_{1} ⪯ σ_{2} indicates that every module in σ_{1} is in σ_{2}.

8. The module signature judgement Γ ⊢_{K} m : σ indicates that the module m has signature σ.

9. The module valuability judgement Γ ⊢_{K} m ↓ σ indicates that the module m has signature σ and that its computation terminates.

Four additional judgements are derived from these nine. A kind is considered well-formed when it is equal to itself, and similarly a constructor belongs to a kind exactly when it is equal to itself in that kind. A type or signature is well-formed when it is a subtype or subsignature of itself.

Γ ⊢_{K} κ kind =^{def} Γ ⊢_{K} κ = κ
Γ ⊢_{K} c : κ =^{def} Γ ⊢_{K} c = c : κ
Γ ⊢_{K} c : type =^{def} Γ ⊢_{K} c ⪯ c
Γ ⊢_{K} σ sig =^{def} Γ ⊢_{K} σ ⪯ σ

2.4 Design Issues

2.4.1 The Phase Distinction

An important decision to be made in the design of any module calculus is how to account for the phase distinction between compile-time and run-time expressions [16, 46, 44]. In the absence of the module calculus, it is easy to say that the evaluation of type constructors is a compile-time activity and the evaluation of terms is a run-time activity. However, the module system presents a quandary since modules may contain terms and type constructors may contain modules (through the extraction construct). Constructor evaluation cannot be a run-time activity, unless we are willing to sacrifice type checking at compile time, but term evaluation clearly cannot be a compile-time activity.

The central problem is how to structure the module system and its interactions with the core calculus so that compile-time expressions (kinds and constructors) do not contain possibly divergent or effectful expressions. I discuss here two solutions to the problem: the phase-splitting approach proposed by Harper, Mitchell and Moggi [46] and the value restriction approach proposed by Harper and Lillibridge [44] and Leroy [62]. Lambda-K makes use of the phase-splitting approach.

In the phase-splitting approach, modules (including functors) are considered to consist of two components: a compile-time component and a run-time component. The compile-time component deals only with the constructor portions of modules while the run-time component deals also with the term portions. Then extraction at the constructor level depends only on the constructor portion of a module; any problematic behavior of the module’s term portion is irrelevant. This approach depends on designing the module calculus so that it is impossible to write a module where a constructor field depends on any run-time computation; otherwise constructor fields are run-time computations themselves and cannot be phase split into the compile-time component.

In a system with first class modules, it is easy to construct a module that depends on the result of a term expression, which rules out the phase-splitting approach. In their translucent sum calculus [44], which has first class modules, Harper and Lillibridge enforce the phase distinction more directly: Syntactic values have no interesting run-time behavior and may therefore safely be treated as compile-time expressions. Extraction at the constructor level is restricted to only valuable expression; arbitrary module expressions are not legal for extraction.

The phase-splitting approach also has some advantages that counterbalance the impossibility of first-class modules: First, phase-splitting provides a convenient mechanism for implementing the module system [46]. Second, phase-splitting allows greater flexibility in references from the core language into the module language. For example, phase-splitting allows extraction from functor applications, which is forbidden by the value restriction approach. This can be quite useful in practice; for example, it allows the expression of transparent signatures for functors [63], that is, signatures that completely specify the (type) behavior of the functor. For example, suppose s has signature Πs′:σ.⟨Type⟩. Then the alternative signature Πs′:σ.⟨ext(s s′)⟩ fully specifies the behavior of s, but this signature is valid only if it is permissible to extract from functor applications. Leroy [63] discusses this issue at length.

2.4.2 Top and Bottom Types

Unlike many subtyping calculi, λ^{K} does not have top and bottom types. This is not because they are problematic to add (they are not), but because a practical language is better off without them. The main use for top and bottom is usually to ensure that there exist meets and joins for any two types, but it is really only important to ensure that joins and meets exist for types that have an upper or lower bound. In cases where two types have no upper (or lower) bound, it is preferable to generate an error than to allow an artificial top (or bottom) bound. For example, a program fragment if b then 12 else true is almost certainly a mistake. Similarly, a function that makes incompatible demands on its argument could be given a bottom domain type, but again such a useless function is certainly a mistake. It is better the notify the programmer of the mistake immediately than to allow the trivial type and postpone the error message, delivering it somewhere else instead of at the location where the mistake was made.

The drawback of this design decision relates to expressions with side-effects. When an expression is evaluated for side-effects, it is not uncommon to ignore the result value. In λ^{K} this must be done explicitly, as in the code if b then f x; () else g x; (), where f and g have incompatible return types. If the language included a top type, the programmer could write the marginally simpler if b then f x else g x. However, even in very impure code, it is not likely that allowing top and bottom would save effort for the programmer in this manner nearly as often as disallowing them would save effort by catching mistakes. Additionally, writing code to ignore result values explicitly provides useful documentation.

Type-Theoretic Semantics

Type theory has become a popular framework for formal reasoning in computer science [22, 67, 37] and has formed the basis for a number of automated deduction systems, including Automath, Nuprl, HOL and Coq [32, 19, 39, 8], among others. In addition to formalizing mathematics, these systems are widely used for the analysis and verification of computer programs. To do this, one must draw a connection between the programming language used and the language of type theory; however, these connections have typically been informal translations, diminishing the significance of the formal verification results.

Formal connections have been drawn in the work of Reynolds [86] and Harper and Mitchell [45], each of whom sought to use type-theoretic analysis to explain an entire programming language. Reynolds gave a type-theoretic interpretation of Idealized Algol, and Harper and Mitchell did the same for a simplified fragment of Standard ML. Recently, Harper and Stone [48] have given such an interpretation of full Standard ML (Revised) [72]. However, in each of these cases, the type theories used were not sufficiently rich to form a foundation for mathematical reasoning; for example, they were unable to express equality or induction principles. On the other hand, Kreitz [59] gave an embedding of a fragment of Objective CAML [64] into the foundational type theory of Nuprl. However, this fragment omitted some important constructs, such as recursion and modules.

The difficulty has been that the same features of foundational type theories that make them so expressive also highly constrain their semantics, thereby restricting the constructs that may be introduced into them. For example, as I will discuss below, the existence of induction principles precludes the typing of fix that is typical in programming languages. In this chapter I show how to give a semantics to practical programming languages in foundational type theory. In particular, I give an embedding of λ^{K} into the Nuprl type theory. This embedding is simple and syntax-directed, which has been vital for its use in practical reasoning.

The applications of type-theoretic semantics are not limited to formal reasoning about programs. Using such a semantics it can be considerably easier to prove desirable properties about a programming language, such as type preservation, than with other means. We will see two such examples in Section 3.4. The usefulness of such semantics is also not limited to one particular programming language at a time. If two languages are given type-theoretic semantics, then one may use type theory to show relationships between the two, and when the semantics are simple, those relationships need be no more complicated than the inherent differences between the two. This is particularly useful in the area of type-directed compilation [93, 75, 61, 43, 77]. The process of type-directed compilation consists of (in part) translations between various typed intermediate languages. Embedding each into a common foundational type theory provides an ideal framework for showing the invariance of program meaning throughout the compilation process.

This semantics is also useful even if one ultimately desires a semantics in some framework other than type theory. Martin-Löf type theory is closely tied to a structured operational semantics and has denotational models in many frameworks including partial equivalence relations [5, 40], set theory [54] and domain theory [87, 81, 80]. Thus, foundational type theory may be used as a “semantic intermediate language.”

3.1 A Computational Denotational Semantics

My main motivation for a type-theoretic semantics has been to draw formal connections between programming languages and type theory, thereby making type theory a powerful tool for reasoning about languages and programs without sacrificing any formality. However, a typetheoretic semantics is also valuable in its own right as mathematical model of a programming language.

Most programming language semantics are either operational or denotational. A typical operational semantics is specified by giving an evaluation relation on program terms (or an evaluation relation on some abstract machine along with a translation into that machine). Operational semantics have the advantage that they draw direct connections to computation, and explaining how programs compute is one of the prime functions of a semantics. However, operational semantics are typically rather brittle; a slight addition or change to an operational semantics often requires reworking all proofs of properties of that semantics.

In contrast, a denotational semantics specifies, for every program term, a mathematical object that the program term denotes. Typically a term’s denotational is determined by composing in some way the denotations of its subterms. The compositionality of denotational semantics usually makes them more robust to change than a typical operational semantics. Furthermore, the equational theory of a denotational semantics is easier to work with since it derives directly from the mathematical objects, without need for an intermediating evaluation relation, and without needing to consider any surrounding context. However, the connection to computation in a typical operational semantics (although present) is much more remote than with an operational semantics.

A denotational semantics in type theory provides the advantages of both a denotational semantics. The type-theoretic semantics I present is denotational, and accrues all the attendant advantages of a denotational semantics, but type theory is in essence a programming language itself (with its own operational semantics), so this semantics also draws a strong connection to computation.

3.2 The Language of Type Theory

I begin with an informal overview of the programming features of the Nuprl type theory. It is primarily those programming features that I will use in the embedding. The logic of types is obtained through the propositions-as-types isomorphism [51], but this will not be critical to our purposes in this chapter. I present and discuss the type theory in detail in Chapter 4.

As base types, the theory contains integers (denoted by ℤ), booleans^{1} (denoted by 𝔹), strings (denoted by Atom), the empty type Void, the trivial type Unit, and the type Top (which contains every well-formed term, and in which all well-formed terms are equal). Complex types are built from the base types using various type constructors such as disjoint unions (denoted by T_{1} + T_{2}), dependent products^{2} (denoted by Σx:T_{1}.T_{2}) and dependent function spaces (denoted by Πx:T_{1}.T_{2}). When x does not appear free in T_{2}, we write T_{1} × T_{2} for Σx:T_{1}.T_{2} and T_{1} → T_{2} for Πx:T_{1}.T_{2}.

1Booleans are actually defined in terms of the disjoint union type.

This gives an account of most of the familiar programming constructs other than polymorphism. To handle polymorphism we want to have functions that can take types as arguments. These can be typed with the dependent types discussed above if one adds a type of all types. Unfortunately, a single type of all types is known to make the theory inconsistent [36, 27, 70, 52], so instead the type theory includes a predicative hierarchy of universes, 𝕌_{1}, 𝕌_{2}, 𝕌_{3}, etc. The universe 𝕌_{1} contains all types built up from the base types only, and the universe 𝕌_{i+1} contains all types built up from the base types and the universes 𝕌_{1}, …, 𝕌_{i}. In particular, no universe is a member of itself.

Unlike λ^{K}, which has distinct syntactic classes for kinds, type constructors and terms, Nuprl has only one syntactic class for all expressions. As a result, types are first class citizens and may be computed just as any other term. For example, the expression if b then ℤ else Top (where b is a boolean expression) is a valid type. Evaluation is call-by-name, but these constructions may also be used in a call-by-value type theory with little modification.

To state the soundness of the embedding of λ^{K}, we will require two assertions from the logic of types. These are equality, denoted by t_{1} = t_{2} in T, which states that the terms t_{1} and t_{2} are equal as members of type T, and subtyping, denoted by T_{1} ⪯ T_{2}, which states that every member of type T_{1} is in type T_{2} (and that terms equal in T_{1} are equal in T_{2}). A membership assertion, denoted by t in T, is defined as t = t in T. The basic judgement in Nuprl is H ⊢_{ν} P, which states that in context H (which contains hypotheses and declarations of variables) the proposition P is true. Often the proposition P will be an assertion of equality or membership in a type.

The basic operators discussed above are summarized in Figure 3.1. Note that the lambda abstractions of Nuprl are untyped, unlike those of λ^{K}. In addition to the operators discussed here, the type theory contains some other less familiar type constructors: the partial type, set type and very dependent function type. In order to better motivate these type constructors, we defer discussion of them until their point of relevance. The dynamic semantics of all the type-theoretic operators is given in Figure 4.3.

3.2.1 Domain and Category Theory

Some of the mechanisms I use in the following section to give the type-theoretic semantics of λ^{K} will look similar to mechanisms used to give programming language semantics in domain or category theory. This is not coincidence; the three theories are closely related and some of the mechanisms I use (such as the partial types of Section 3.3.2) are adapted from the folklore of domain theory.

However, domain theory and category theory are not interchangeable with type theory for the purposes in this dissertation. Type theory provides the highest degree of structure of the three theories, domain theory hides some structure in the interest of greater abstraction and generality, and category theory provides the least structure and the most abstraction and generality. Thus, one can easily construct a domain or a category based on the Nuprl type

2These are sometimes referred to in the literature as dependent sums, but I prefer the terminology to suggest the connection to the non-dependent type T_{1} × T_{2}.

Type Formation  Introduction  Elimination
universe i  𝕌_{i}  type formation
(for i ≥ 1)  operators
disjoint union T_{1} + T_{2} inj_{1}(e) case(e, x_{1}.e_{1}, x_{2}.e_{2})
inj_{2}(e)
function space Πx:T_{1}.T_{2}  λx.e  e_{1}e_{2}
product space Σx:T_{1}.T_{2}  ⟨e_{1}, e_{2}⟩  π_{1}(e)

π_{2}(e)

integers  ℤ  …, −1, 0, 1, 2, … assorted operations
booleans  𝔹  true, false  if-then-else
atoms  Atom  string literals  equality test (=_{A})
void  Void
unit  Unit  ⋆
top  Top

Figure 3.1: Type Theory Syntax

theory, but one cannot easily work backwards (although such models of type theory do exist [87, 81, 80]).

The structure of the Nuprl type theory stems from the fact that Nuprl provides direct access to the primitives of computation and the types of Nuprl speak directly of the computational behavior of terms. This structure is essential for achieving my goal of establishing a direct computational significance for this semantics (recall Section 3.1). This structure also allows type theory to address directly issues that pose significant challenges for domain theory (and that are not directly meaningful in category theory). For example, in type theory we may easily include or exclude divergent terms from types, allowing us to easily distinguish between partial or total functions, and, more importantly, allowing us to use induction properties that are invalid if one includes divergent terms.

3.3 A Type-Theoretic Semantics

I present the embedding of λ^{K} into type theory in three parts. In the first part I begin by giving embeddings for most of the basic type and term operators. These embeddings are uniformly straightforward. Second, I examine what happens when the embedding is expanded to include fix. There we will find it necessary to modify some of the original embeddings of the basic operators. In the third part I complete the semantics by giving embeddings for the kind-level constructs of λ^{K}. The complete embedding is summarized in Figures 3.3 through 3.6.

The embedding itself could be formulated in type theory, leaving to metatheory only the trivial task of encoding the abstract syntax of the programming language. Were this done, the theorems of Section 3.4 could be proven within the framework of type theory. For simplicity, however, I will state the embedding and theorems in metatheory.

3.3.1 Basic Embedding

The embedding is defined as a syntax-directed mapping (denoted by [[ · ]]) of λ^{K} expressions to terms of type theory. Recall that in Nuprl all expressions are terms; in particular, types are terms and may be computed just as any other term. Many λ^{K} expressions are translated directly into type theory:

[[x]] =^{def} x
[[α]] =^{def} α
[[λx:c.e]] =^{def} λx.[[e]]
[[e_{1}e_{2}]] =^{def} [[e_{1}]][[e_{2}]]
[[c_{1} → c_{2}]] =^{def} [[c_{1}]] → [[c_{2}]]

Nothing happens here except that the types are stripped out of lambda abstractions to match the syntax of Nuprl. Functions at the type constructor level are equally easy to embed, but I defer discussion of them until Section 3.3.3.

Since the type theory does not distinguish between functions taking term arguments and functions taking type arguments, polymorphic functions may be embedded just as easily, although a dependent type is required to express the dependency of c on α in the polymorphic type ∀α:κ.c:

[[Λα:κ.e]] =^{def} λα.[[e]]
[[e[c]]] =^{def} [[e]][[c]]
[[∀α:κ.c]] =^{def} Πα:[[κ]].[[c]]

Just as the type was stripped out of the lambda abstraction above, the kind is stripped out of the polymorphic abstraction. The translation of the polymorphic function type above makes use of the embedding of kinds, but, except for the elementary kind Type, I defer discussion of the embedding of kinds until Section 3.3.3. The kind Type_{i}, which contains level-i types, is embedded as the universe containing level-i types:

[[Type_{i}]] =^{def} 𝕌_{i}

Records and Disjoint Unions A bit more delicate than the above, but still fairly simple, is the embedding of records. Field labels are taken to be members of type Atom, and then records are viewed as functions that map field labels to the contents of the corresponding fields. For example, the record {x = 1, f = λx:int.x}, which has type {x : int, f : int → int}, is embedded as

λa. if a =_{A} x then 1 else if a =_{A} f then λx.x else ⋆

where a =_{A} a′ is the equality test on atoms, which returns a boolean when a and a′ are atoms.

Since the type of this function’s result depends upon its argument, this function must be typed using a dependent type:

Πa:Atom. if a =_{A} x then ℤ else if a =_{A} f then ℤ → ℤ else Top

In general, records and record types are embedded as follows:

[[{ℓ_{1} = e_{1}, …, ℓ_{n} = e_{n}}]] =^{def} λa. if a =_{A} ℓ_{1} then [[e_{1}]]
⋮
else if a =_{A} ℓ_{n} then [[e_{n}]]
else ⋆
[[π_{ℓ}(e)]] =^{def} [[e]] ℓ
[[{ℓ_{1} : c_{1}, …, ℓ_{n} : c_{n}}]] =^{def} Πa:Atom. if a =_{A} ℓ_{1} then [[c_{1}]]
⋮
else if a =_{A} ℓ_{n} then [[c_{n}]]
else Top

Note that this embedding validates the desired subtyping relationship on records. Since {x : int, f : int→int} ⪯ {x : int}, we would like the embedding to respect the subtyping relationship: [[{x : int, f : int → int}]] ⪯ [[{x : int}]]. Fortunately this is the case, since every type is a subtype of Top, and in particular the part of the type relating to the omitted field, if a = f then ℤ → ℤ else Top, is a subtype of Top. The use of a type Top to catch extra labels is essential for subtyping to work properly makes for a particularly elegant embedding of records, but it is not essential. In the absence of Top one could produce a slightly less elegant embedding by restricting the domain to exclude undesired labels using a set type (Section 3.3.3).

Disjoint unions are handled in a similar manner. The injection term inj_{x}(1) is embedded as the pair ⟨x, 1⟩ of its label and its argument. The types of this term include the sum type ⟨x : int, f : int → int⟩, which is embedded using a dependent type as:

Σa:Atom. if a =_{A} x then ℤ else if a =_{A} f then ℤ → ℤ else Void

In general, disjoint unions are embedded as follows:

[[⟨ℓ_{1} : c_{1}, …, ℓ_{n} : c_{n}⟩]] =^{def} Σa:Atom. if a =_{A} ℓ_{1} then [[c_{1}]]
⋮
else if a =_{A} ℓ_{n} then [[c_{n}]]
else Void
[[inj_{ℓ}(e)]] =^{def} ⟨ℓ, e⟩
[[ case(e, ℓ_{1} ▷ x_{1}.e_{1}, …, ℓ_{n} ▷ x_{n}.e_{n})]] =^{def} if π_{1}(x) =_{A} ℓ_{1} then [[e_{1}]][π_{2}(x)/x_{1}]
⋮
else if π_{1}(x) =_{A} ℓ_{n} then [[e_{n}]][π_{2}(x)/x_{n}]
else ⋆

Again, this relation validates the desired subtyping relationship.

3.3.2 Embedding Recursion

A usual approach to typing general recursive definitions of functions, and the one used in λ^{K}, is to add a fix construct with the typing rule:

{H ⊢_{ν} e in T → T \atop H ⊢_{ν} fix(e) in T}  (wrong)

In effect, this adds recursively defined (and possibly divergent) terms to existing types. Unfortunately, such a broad fixpoint rule makes Martin-Löf type theories inconsistent because of the presence of induction principles. An induction principle on a type specifies the membership of that type; for example, the standard induction principle on the natural numbers specifies that every natural number is either zero or some finite iteration of successor on zero. The ability to add divergent elements to a type would violate the specification implied by that type’s induction rule.

One simple way to derive an inconsistency from the above typing rule uses the simplest induction principle, induction on the empty type Void. The induction principle for Void indirectly specifies that it has no members:

H ⊢_{ν} e in Void
H ⊢_{ν} e in T

However, it would be easy, using fix, to derive a member of Void: the identity function can be given type Void → Void, so fix(λx.x) would have type Void. Invoking the induction principle, fix(λx.x) would be a member of every type and, by the propositions-as-types isomorphism, would be a proof of every proposition. It is also worth noting that this inconsistency does not stem from the fact that Void is an empty type; similar inconsistencies may be derived (with a bit more work) for almost every type, including function types (to which the fix rule of λ^{K} is restricted).

It is clear, then, that fix cannot be used to define new members of the basic types. How then can recursive functions be typed? The solution is to add a new type constructor for partial types [24, 25, 92]. For any type T, the partial type \overline{T} is a supertype of T that contains all the elements of T and also all divergent terms. (A total type is one that contains only convergent terms.) The induction principles on \overline{T} are different than those on T, so we can safely type fix with the rule:^{3}

H ⊢_{ν} e in \overline{T} → \overline{T} H ⊢_{ν} T admiss
H ⊢_{ν} fix(e) in \overline{T}

We use partial types to interpret the possibly non-terminating computations of λ^{K}. When (in λ^{K}) a term e has type τ, the embedded term [[e]] will have type \overline{[[τ]]}. Moreover, if e is valuable, then [[e]] can still be given the stronger type [[τ]]. Before we can embed fix we must re-examine the embedding of function types. In Nuprl, partial functions are viewed as functions with partial result types:^{4}

[[c_{1} → c_{2}]] =^{def} [[c_{1}]] → \overline{[[c_{2}]]}
[[c_{1} ⇒ c_{2}]] =^{def} [[c_{1}]] → [[c_{2}]]
[[∀α:κ.c]] =^{def} Πα:[[κ]].[[c]]

Note that, as desired, [[τ_{1} ⇒ τ_{2}]] ⪯ [[τ_{1} → τ_{2}]], since [[τ_{2}]] ⪯ \overline{[[τ_{2}]]}. If partial polymorphic functions were included in λ^{K}, they would be embedded as Πα:[[κ]].\overline{[[c]]}.

Now suppose we wish to fix the function f which (in λ^{K}) has type (τ_{1} →τ_{2})→(τ_{1} →τ_{2}), and suppose, for simplicity only, that f is valuable. Then [[f]] has type ([[τ_{1}]] → \overline{[[τ_{2}]]}) → \overline{[[τ_{1}]] → \overline{[[τ_{2}]]}}.

3The second subgoal, that the type T be admissible, is a technical condition related to the notion of admissibility in LCF [38]. All the types used in the embedding are admissible, so I ignore the admissibility condition in the discussion of this chapter. Admissibility is discussed further in Chapter 4 and is examined in detail in Chapter 5.

4This terminology can be somewhat confusing. A total type is one that contains only convergent expressions.

The partial function type T_{1} →\overline{T}_{2} contains functions that return possibly divergent elements, but those functions themselves converge, so a partial function type is a total type.

This type does not quite fit the fix typing rule, which requires the domain type to be partial, so we must coerce [[f]] to a fixable type. We do this by eta-expanding [[f]] to gain access to its argument (call it g) and then eta-expanding that argument g:

λg.[[f]](λx.g x) in \overline{[[τ_{1}]] → \overline{[[τ_{2}]]}} → \overline{[[τ_{1}]] → \overline{[[τ_{2}]]}}

Eta-expanding g ensures that it terminates (since λx.g x is a canonical form), changing its type from \overline{[[τ_{1}]] → \overline{[[τ_{2}]]}} to [[τ_{1}]] → \overline{[[τ_{2}]]}. The former type is required by the fix rule, but the latter type is expected by [[f]]. Since the coerced [[f]] fits the fix typing rule, we get that fix(λg.[[f]](λx.g x)) has type \overline{[[τ_{1}]] → \overline{[[τ_{2}]]}}, as desired. Thus we may embed the fix construct as:

[[ fix_{c}(e)]] =^{def} fix(λg.[[e]](λx.g x))

Strictness In λ^{K}, a function may be applied to a possibly divergent argument, but in my semantics functions expect their arguments to be convergent. Therefore we must change the embedding of application to compute function arguments to canonical form before applying the function.^{5} This is done using the sequencing construct let x = e_{1} in e_{2} which evaluates e_{1} to canonical form e′_{1} and then reduces to e_{2}[e′_{1}/x]. The sequence term diverges if e_{1} or e_{2} does and allows x be given a total type:

H ⊢_{ν} e_{1} in \overline{T}_{2} H; x:T_{2} ⊢_{ν} e_{2} in \overline{T_{1}}
H ⊢_{ν} let x = e_{1} in e_{2} in \overline{T_{1}}

Application is then embedded in the expected way:

[[e_{1}e_{2}]] =^{def} let x = [[e_{2}]] in [[e_{1}]] x

A final issue arises in regard to records and disjoint unions. In the embedding of Section 3.3.1, the record {ℓ = e} would terminate even if e diverges. This would be unusual in a call-by-value programming language, so we need to ensure that each member of a record is evaluated:

[[{ℓ_{1} = e_{1}, …, ℓ_{n} = e_{n}}]] =^{def} let x_{1} = [[e_{1}]] in
⋮
let x_{n} = [[e_{n}]] in
λa. if a =_{A} ℓ_{1} then x_{1}
⋮
else if a =_{A} ℓ_{n} then x_{n}
else ⋆

A similar change must be also be made for disjoint unions:

[[inj_{ℓ}(e)]] =^{def} let x = [[e]] in ⟨ℓ, x⟩

5Polymorphic functions are unaffected because all type expressions converge (Corollary 3.4).

3.3.3 Embedding Kinds

The kind structure of λ^{K} contains three first-order kind constructors. We have already seen the embedding of the kind Type; remaining are the power and singleton kinds. Each of these kinds represents a collection of types, so each will be embedded as something similar to a universe, but unlike the kind Type_{i}, which includes all types of the indicated level, the power and singleton kinds wish to exclude certain undesirable types. The power kind P_{i}(τ) contains only subtypes of τ and the singleton kind S_{i}(τ) contains only types that are equal to τ; other types must be left out.

The mechanism for achieving this exclusion is the set type [21]. If S is a type and P [·] is a predicate over S, then the set type {z : S | P [z]} contains all elements z of S such that P [z] is true. With this type, we can embed the power and singleton kinds as:^{6}

[[P_{i}(c)]] =^{def} {T : 𝕌_{i} | T ⪯ [[c]] ∧ [[c]] in 𝕌_{i}}
[[S_{i}(c)]] =^{def} {T : 𝕌_{i} | T = [[c]] in 𝕌_{i}}

Among the higher-order type constructors, functions at the type constructor level and their kinds are handled just as at the term level, except that function kinds are permitted to have dependencies but need not deal with partiality or strictness:

[[λα:κ.c]] =^{def} λα.[[c]]
[[c_{1}[c_{2}]]] =^{def} [[c_{1}]][[c_{2}]]
[[Πα:κ_{1}.κ_{2}]] =^{def} Πα:[[κ_{1}]].[[κ_{2}]]

Dependent Record Kinds For records at the type constructor level, the embedding of the records themselves is analogous to those at the term level (except that there is no issue of strictness):

[[{ℓ_{1} = c_{1}, …, ℓ_{n} = c_{n}}]] =^{def} λa. if a =_{A} ℓ_{1} then [[c_{1}]]
⋮
else if a =_{A} ℓ_{n} then [[c_{n}]]
else ⋆
[[π_{ℓ}(c)]] =^{def} [[c]] ℓ

However, the embedding of this expression’s kind is more complicated. This is because of the need to express dependencies among the fields of the dependent record kind. Recall that the embedding of a non-dependent record type already required a dependent type; to embed a dependent record type will require expressing even more dependency. Consider the dependent record kind {ℓ ▷ α : Type_{1}, ℓ′ ▷ α′ : P_{1}(α)}. We might naively attempt to encode this like the non-dependent record type as

Πa:Atom. if a =_{A} ℓ then 𝕌_{1} else if a =_{A} ℓ′ then {T : 𝕌_{1} | T ⪯ α ∧ α in 𝕌_{1}} else Top (wrong)

but this encoding is not correct; the variable α is now unbound. We want α to refer to the contents of field ℓ. In the encoding, this means we want α to refer to the value returned by the function when applied to label ℓ. So we want a type of functions whose return type can depend not only upon their arguments but upon their own return values!

6The second clause in the embedding of the power kind ([[c]] in 𝕌_{i}) is used for technical reasons that require that well-formedness of P_{i}(τ) imply that τ : Type_{i}.

The type I will use for this embedding is Hickey’s very dependent function type [49]. This type is a generalization of the dependent function type (itself a generalization of the ordinary function type) and like it, the very dependent function type’s members are just lambda abstractions. The difference is in the specification of a function’s return type. The type is denoted by {f | x:T_{1} → T_{2}} where f and x are binding occurrences that may appear free in T_{2} (but not in T_{1}).

As with the dependent function type, x stands for the function’s argument, but the additional variable f refers to the function itself. A function g belongs to the type {f | x:T_{1} → T_{2}} if g takes an argument from T_{1} (call it t) and returns a member of T_{2}[t, g/x, f].^{7}

For example, the kind {ℓ ▷ α : Type_{1}, ℓ′ ▷ α′ : P_{1}(α)} discussed above is encoded as a very dependent function type as:

{f | a:Atom → if a =_{A} ℓ then 𝕌_{1} else if a =_{A} ℓ′ then {T : 𝕌_{1} | T ⪯ f ℓ ∧ f ℓ in 𝕌_{1}} else Top}

To understand where this type constructor fits in with the more familiar type constructors, consider the “type triangle” shown in Figure 3.2. On the right are the non-dependent type constructors and in the middle are the dependent type constructors. Arrows are drawn from type constructors to weaker ones that may be implemented with them. Horizontal arrows indicate when a weaker constructor may be obtained by dropping a possible dependency from a stronger one; for example, the function type T_{1} → T_{2} is a degenerate form of the dependent function type Πx:T_{1}.T_{2} where the dependent variable x is not used in T_{2}. Diagonal arrows indicate when a weaker constructor may be implemented with a stronger one by performing case analysis on a boolean; for example, the disjoint union type T_{1} + T_{2} is equivalent to the type Σb:𝔹. if b then T_{1} else T_{2}.^{8}

If we ignore the very dependent function type, the type triangle illustrates how the basic type constructors may be implemented by the dependent function and dependent product types. The very dependent function type completes this picture: the dependent function is a degenerate form where the f dependency is not used, and the dependent product may be implemented by switching on a boolean. Thus, the very dependent function type is a single unified type constructor from which all the basic type constructors may be constructed.

In general, dependent record kinds are encoded using a very dependent function type as follows:

[[{ℓ_{1} ▷ α_{1} : κ_{1}, …, ℓ_{n} ▷ α_{n} : κ_{n}}]] =^{def} {f | a:Atom→
if a =_{A} ℓ_{1} then [[κ_{1}]]
else if a =_{A} ℓ_{2} then [[κ_{2}]][f ℓ_{1}/α_{1}]
⋮
else if a =_{A} ℓ_{n} then
[[κ_{n}]][f ℓ_{1} ⋯ f ℓ_{n−1}/α_{1} ⋯ α_{n−1}]
else Top}

7To avoid the apparent circularity, in order for {f | x:T_{1} → T_{2}} to be well-formed we require that T_{2} may only use the result of f when applied to elements of T_{1} that are less than x with regard to some well-founded order.

This restriction will not be a problem for this embedding because the order in which field labels appear in a dependent record kind is a perfectly good well-founded order.

8By switching on a label, instead of a boolean, record types and tagged variant types could be implemented and placed along the diagonals as well.

[figure: {f | x:T_{1} → T_{2}}; Πx:T_{1}.T_{2}; Σx:T_{1}.T_{2}; T_{1} → T_{2}; T_{1} × T_{2}; T_{1} + T_{2}]

Figure 3.2: The Type Triangle

3.3.4 Embedding Modules

As discussed in Section 2.4.1, λ^{K} uses a phase-splitting interpretation of modules, where modules (including higher-order modules) are considered to consist of two components: a compile-time component and a run-time component. This is reflected in the type-theoretic semantics by an embedding that explicitly phase-splits modules. The technique used is derived from Harper, et al. [46].

The embeddings for modules and signatures are given by two syntax-directed mappings, [[ ·]]_{c} and [[ ·]]_{r}, one for the compile-time component of the given expression and one for the run time component. Given these, the embedding of a module is a pair of the compile-time and run-time components:

[[m]] =^{def} ⟨[[m]]_{c}, [[m]]_{r}⟩

In signatures, the types of run-time fields may depend upon a compile-time member, as in the signature {foo ▷ s_{foo} : ⟨Type⟩, bar : ⟨⟨ext(s_{foo})⟩⟩} (corresponding to the KML module sig tycon foo : type val bar : foo end). Consequently, the embedding of a signature is the dependent product of the compile-time component and the run-time component. The run-time component’s embedding is a function that takes as an argument the compile-time member on which it depends. In the embedding of the full signature, that function is applied to the variable standing for the compile-time member:

[[σ]] =^{def} Σv:[[σ]]_{c}.[[σ]]_{r}v

The embeddings of basic modules and signatures are simple. The run-time component is trivial for ⟨κ⟩ signatures and ⟨c⟩ modules, and the compile-time component is trivial for ⟨⟨c⟩⟩ signatures and ⟨⟨e⟩⟩. Module variables s are split into separate variables s_{c} and s_{r} for each component.

[[⟨c⟩]]_{c} =^{def} [[c]]  (module compile-time component)
[[⟨c⟩]]_{r} =^{def} ⋆  (module run-time component—trivial)
[[⟨κ⟩]]_{c} =^{def} [[κ]]  (signature compile-time component)
[[⟨κ⟩]]_{r} =^{def} λv.Top (signature run-time component—trivial)
[[⟨⟨e⟩⟩]]_{c} =^{def} ⋆  (module compile-time component—trivial)
[[⟨⟨e⟩⟩]]_{r} =^{def} [[e]]  (module run-time component)
[[⟨⟨c⟩⟩]]_{c} =^{def} Top  (signature compile-time component—trivial)
[[⟨⟨c⟩⟩]]_{r} =^{def} λv.[[c]] (signature run-time component)
[[s]]_{c} =^{def} s_{c}  (module compile-time component)
[[s]]_{r} =^{def} s_{r}  (module run-time component)

For each of the signatures above it is impossible for there to be any dependency of the run-time component on the compile-time component, since for ⟨κ⟩ there is no nontrivial run-time to depend on anything, and for ⟨⟨c⟩⟩ there is no nontrivial compile-time for anything to depend on. As a result, the variable v is ignored in each case.

Functors For function modules and signatures, everything looks familiar in the compile-time component, where the run-time material is ignored:

[[λs:σ.m]]_{c} =^{def} λs_{c}.[[m]]_{c}
[[m_{1}m_{2}]]_{c} =^{def} [[m_{1}]]_{c}[[m_{2}]]_{c}
[[Πs:σ_{1}.σ_{2}]]_{c} =^{def} Πs_{c}:[[σ_{1}]]_{c}.[[σ_{2}]]_{c}

However, the run-time component may not similarly ignore the compile-time component, because of possible dependencies. Therefore, the run-time component abstracts over both the compile-time and run-time components of its argument:

[[λs:σ.m]]_{r} =^{def} λs_{c}. λs_{r}. [[m]]_{r}
[[m_{1}m_{2}]]_{r} =^{def} let x = [[m_{2}]]_{r} in [[m_{1}]]_{r}[[m_{2}]]_{c}x

The signature’s run-time component, then, takes an argument v representing the compile-time component and returns a curried function type. The result type is the run-time component of the result signature and that depends on the compile-time component. Fortunately, the result’s compile-time component is available (as v s_{c}), since v maps the compile-time component of the argument to the compile-time component of the result.

[[Πs:σ_{1}.σ_{2}]]_{r} =^{def} λv. Πs_{c}:[[σ_{1}]]_{c}. [[σ_{1}]]_{r}s_{c} → [[σ_{2}]]_{r}(v s_{c})

Structures For dependent record modules and signatures, the compile-time component looks like dependent record kinds and the type constructor records that belong to them:

[[{ℓ_{1} = m_{1}, …, ℓ_{n} = m_{n}}]]_{c} =^{def} λa. if a =_{A} ℓ_{1} then [[m_{1}]]_{c} ⋮ else if a =_{A} ℓ_{n} then [[m_{n}]]_{c} else ⋆

[[π_{ℓ}(m)]]_{c} =^{def} [[m]]_{c}ℓ
[[{ℓ_{1} ▷ s_{1} : σ_{1}, …, ℓ_{n} ▷ s_{n} : σ_{n}}]]_{c} =^{def} {f | a:Atom → if a =_{A} ℓ_{1} then [[σ_{1}]]_{c} else if a =_{A} ℓ_{2} then [[σ_{2}]]_{c}[f ℓ_{1}/s_{1c}] ⋮ else if a =_{A} ℓ_{n} then [[σ_{n}]]_{c}[f ℓ_{1} ⋯ f ℓ_{n−1}/s_{1c} ⋯ s_{(n−1)c}] else Top}

The run-time component of modules looks much like the embedding of record terms (as with those, a series of opening lets is necessary to ensure strictness):

[[{ℓ_{1} = m_{1}, …, ℓ_{n} = m_{n}}]]_{r} =^{def} let x_{1} = [[m_{1}]]_{r} in

⋮

let x_{n} = [[m_{n}]]_{r} in

λa. if a =_{A} ℓ_{1} then x_{1} ⋮ else if a =_{A} ℓ_{n} then x_{n} else ⋆

(where x_{i} does not appear free in m_{i})

[[π_{ℓ}(m)]]_{r} =^{def} [[m]]_{r}ℓ

The run-time component of signatures is also familiar, except that it must deal with dependencies on the compile-time component. Again, the run-time component takes an argument v representing the compile-time component. The run-time component [[σ_{i}]]_{r} of each field is applied to the compile-time component of that field, which is v ℓ_{i}. Also, each field may have dependencies on the compile-time components of earlier fields. These dependencies will have been expressed by free occurrences of the variables s_{ic}, into which we substitute the corresponding compile-time components v ℓ_{i}. It is worthwhile to note that these substitutions for s_{ic} are the only places where dependencies on the argument v are introduced.

[[{ℓ_{1} ▷ s_{1} : σ_{1}, …, ℓ_{n} ▷ s_{n} : σ_{n}}]]_{r}
=^{def} λv. Πa:Atom. if a =_{A} ℓ_{1} then [[σ_{1}]]_{r}(v ℓ_{1}) if a =_{A} ℓ_{2} then [[σ_{2}]]_{r}(v ℓ_{2})[v ℓ_{1}/s_{1c}] ⋮ else if a =_{A} ℓ_{n} then [[σ_{n}]]_{r}(v ℓ_{n})[v ℓ_{1} ⋯ v ℓ_{n−1}/s_{1c} ⋯ s_{(n−1)c}] else Top

[[Type_{i}]] =^{def} {T : 𝕌_{i} | T admiss ∧ T total}
[[Πα:κ_{1}.κ_{2}]] =^{def} Πα:[[κ_{1}]].[[κ_{2}]]
[[{ℓ_{1} ▷ α_{1} : κ_{1}, …, ℓ_{n} ▷ α_{n} : κ_{n}}]] =^{def} {f | a:Atom → if a =_{A} ℓ_{1} then [[κ_{1}]] else if a =_{A} ℓ_{2} then [[κ_{2}]][f ℓ_{1}/α_{1}] ⋮ else if a =_{A} ℓ_{n} then [[κ_{n}]][f ℓ_{1} ⋯ f ℓ_{n−1}/α_{1} ⋯ α_{n−1}] else Top}

(where f, a do not appear free in κ_{i})

[[P_{i}(c)]] =^{def} {T : 𝕌_{i} | T ⪯ [[c]] ∧ [[c]] in 𝕌_{i} ∧ T admiss}

(where T does not appear free in c)

[[S_{i}(c)]] =^{def} {T : 𝕌_{i} | T = [[c]] in 𝕌_{i}}

(where T does not appear free in c)

[[α]] =^{def} α
[[λα:κ.c]] =^{def} λα.[[c]]
[[c_{1}[c_{2}]]] =^{def} [[c_{1}]][[c_{2}]]
[[{ℓ_{1} = c_{1}, …, ℓ_{n} = c_{n}}]] =^{def} λa. if a =_{A} ℓ_{1} then [[c_{1}]]

⋮

else if a =_{A} ℓ_{n} then [[c_{n}]] else ⋆

(where a does not appear free in c_{i})

[[π_{ℓ}(c)]] =^{def} [[c]] ℓ
[[c_{1} → c_{2}]] =^{def} [[c_{1}]] → \overline{[[c_{2}]]}
[[c_{1} ⇒ c_{2}]] =^{def} [[c_{1}]] → [[c_{2}]]
[[∀α:κ.c]] =^{def} Πα:[[κ]].[[c]]
[[{ℓ_{1} : c_{1}, …, ℓ_{n} : c_{n}}]] =^{def} Πa:Atom. if a =_{A} ℓ_{1} then [[c_{1}]]
⋮

else if a =_{A} ℓ_{n} then [[c_{n}]]

else Top

(where a does not appear free in c_{i})

[[⟨ℓ_{1} : c_{1}, …, ℓ_{n} : c_{n}⟩]] =^{def} Σa:Atom. if a =_{A} ℓ_{1} then [[c_{1}]]
⋮

else if a =_{A} ℓ_{n} then [[c_{n}]]

else Void

(where a does not appear free in c_{i})

[[ ext(m)]] =^{def} [[m]]_{c}

Figure 3.3: Embedding Kinds and Types

[[x]] =^{def} x
[[λx:c.e]] =^{def} λx.[[e]]
[[e_{1}e_{2}]] =^{def} let x = [[e_{2}]] in [[e_{1}]] x

(where x does not appear free in e_{1})

[[Λα:κ.e]] =^{def} λα.[[e]]
[[e[c]]] =^{def} [[e]][[c]]
[[{ℓ_{1} = e_{1}, …, ℓ_{n} = e_{n}}]] =^{def} let x_{1} = [[e_{1}]] in

⋮

let x_{n} = [[e_{n}]] in

λa. if a =_{A} ℓ_{1} then x_{1}

⋮
else if a =_{A} ℓ_{n} then x_{n}
else ⋆

(where x_{i} does not appear free in e_{j})

[[π_{ℓ}(e)]] =^{def} [[e]] ℓ
[[inj_{ℓ}(e)]] =^{def} let x = [[e]] in ⟨ℓ, x⟩
[[ case(e, ℓ_{1} ▷ x_{1}.e_{1}, …, ℓ_{n} ▷ x_{n}.e_{n})]] =^{def} let x = [[e]] in

if π_{1}(x) =_{A} ℓ_{1} then e_{1}[π_{2}(x)/x_{1}]

⋮

else if π_{1}(x) =_{A} ℓ_{n} then e_{n}[π_{2}(x)/x_{n}] else ⋆ (where x does not appear free in e_{i})

[[ fix_{c}(e)]] =^{def} fix(λg.[[e]](λx.g x))

(where g does not appear free in e)

[[ ext(m)]] =^{def} [[m]]_{r}

Figure 3.4: Embedding Terms

3.4 Properties of the Embedding

I conclude my presentation of the type-theoretic semantics of λ^{K} by examining some of the important properties of the semantics. We want the embedding to validate the intuitive meaning of the judgements of λ^{K}’s static semantics. If κ_{1} is a subkind of κ_{2} then we want the embedded kind [[κ_{1}]] to be a subtype of [[κ_{2}]]; if c_{1} and c_{2} are equal in kind κ, we want the embedded constructors [[c_{1}]] and [[c_{2}]] to be equal (in [[κ]]); and if e has type τ we want [[e]] to have type \overline{[[τ]]} (and [[τ]] if e is valuable). Similar properties are desired for the module judgements. This is stated in Theorem 3.1:

Theorem 3.1 (Semantic Soundness) For every λ^{K} context Γ, let [[Γ]] be defined as follows:

[[ •]] =^{def} ϵ
[[Γ[α : κ]]] =^{def} [[Γ]]; α:[[κ]]
[[Γ[x : c]]] =^{def} [[Γ]]; x:[[c]]
[[Γ[s : σ]]] =^{def} [[Γ]]; s_{c}:[[σ]]_{c}; s_{r}:[[σ]]_{r}s_{c}

Then the following implications hold:

1. If Γ ⊢_{K} κ = κ′ then level(κ) = level(κ′) and [[Γ]] ⊢_{ν} [[κ]] = [[κ′]] in 𝕌_{level(κ)+1}

[[σ]] =^{def} Σv:[[σ]]_{c}.[[σ]]_{r}v

(where v does not appear free in σ)

[[⟨κ⟩]]_{c} =^{def} [[κ]]
[[⟨κ⟩]]_{r} =^{def} λv.Top
[[⟨⟨c⟩⟩]]_{c} =^{def} Top
[[⟨⟨c⟩⟩]]_{r} =^{def} λv.[[c]]

(where v does not appear free in c)

[[Πs:σ_{1}.σ_{2}]]_{c} =^{def} Πs_{c}:[[σ_{1}]]_{c}.[[σ_{2}]]_{c}
[[Πs:σ_{1}.σ_{2}]]_{r} =^{def} λv. Πs_{c}:[[σ_{1}]]_{c}. [[σ_{1}]]_{r}s_{c} → [[σ_{2}]]_{r}(v s_{c})

(where v, s do not appear free in σ_{i})

[[{ℓ_{1} ▷ s_{1} : σ_{1}, …, ℓ_{n} ▷ s_{n} : σ_{n}}]]_{c} =^{def} {f | a:Atom → if a =_{A} ℓ_{1} then [[σ_{1}]]_{c} else if a =_{A} ℓ_{2} then [[σ_{2}]]_{c}[f ℓ_{1}/s_{1c}] ⋮ else if a =_{A} ℓ_{n} then [[σ_{n}]]_{c}[f ℓ_{1} ⋯ f ℓ_{n−1}/s_{1c} ⋯ s_{n−1c}] else Top}

(where f, a do not appear free in σ_{i})

[[{ℓ_{1} ▷ s_{1} : σ_{1}, …, ℓ_{n} ▷ s_{n} : σ_{n}}]]_{r} =^{def} λv. Πa:Atom. if a =_{A} ℓ_{1} then [[σ_{1}]]_{r}(v ℓ_{1}) if a =_{A} ℓ_{2} then [[σ_{2}]]_{r}(v ℓ_{2})[v ℓ_{1}/s_{1c}] ⋮ else if a =_{A} ℓ_{n} then [[σ_{n}]]_{r}(v ℓ_{n}) [v ℓ_{1} ⋯ v ℓ_{n−1}/s_{1c} ⋯ s_{n−1c}] else Top}

(where v, a do not appear free in σ_{i})

Figure 3.5: Embedding Signatures

2. If Γ ⊢_{K} κ ⪯ κ′ then [[Γ]] ⊢_{ν} ([[κ]] in 𝕌_{level(κ)+1} ∧ [[κ′]] in 𝕌_{level(κ′)+1} ∧ [[κ]] ⪯ [[κ′]]).

3. If Γ ⊢_{K} c = c′ : κ then [[Γ]] ⊢_{ν} [[c]] = [[c′]] in [[κ]].

4. If Γ ⊢_{K} c ⪯ c′ then [[Γ]] ⊢_{ν} [[c]] ⪯ [[c′]].

5. If Γ ⊢_{K} e : c then [[Γ]] ⊢_{ν} [[e]] in \overline{[[c]]}.

6. If Γ ⊢_{K} e ↓ c then [[Γ]] ⊢_{ν} [[e]] in [[c]].

7. If Γ ⊢_{K} σ ⪯ σ′ then [[Γ]] ⊢_{ν} ([[σ]] in 𝕌_{level(σ)+1} ∧ [[σ′]] in 𝕌_{level(σ′)+1} ∧ [[σ]] ⪯ [[σ′]]).

8. If Γ ⊢_{K} m : σ then [[Γ]] ⊢_{ν} ([[m]] in \overline{[[σ]]} ∧ [[m]]_{c} in [[σ]]_{c}).

9. If Γ ⊢_{K} m ↓ σ then [[Γ]] ⊢_{ν} [[m]] in [[σ]]. Proof

By induction on the derivations of the λ^{K} judgements. We may observe two immediate consequences of the soundness theorem. One is the desirable

property of type preservation: evaluation does not change the type of a program. Figure 4.3 gives a small-step evaluation relation for the Nuprl type theory (denoted by t ↦ t′ when t evaluates in one step to t′). Type preservation of λ^{K} (Corollary 3.3) follows directly from soundness and type preservation of Nuprl (Proposition 3.2).

[[m]] =^{def} ⟨[[m]]_{c}, [[m]]_{r}⟩
[[s]]_{c} =^{def} s_{c}
[[s]]_{r} =^{def} s_{r}
[[⟨c⟩]]_{c} =^{def} [[c]]
[[⟨c⟩]]_{r} =^{def} ⋆
[[⟨⟨e⟩⟩]]_{c} =^{def} ⋆
[[⟨⟨e⟩⟩]]_{r} =^{def} [[e]]
[[λs:σ.m]]_{c} =^{def} λs_{c}.[[m]]_{c}
[[λs:σ.m]]_{r} =^{def} λs_{c}. λs_{r}. [[m]]_{r}
[[m_{1}m_{2}]]_{c} =^{def} [[m_{1}]]_{c}[[m_{2}]]_{c}
[[m_{1}m_{2}]]_{r} =^{def} let x = [[m_{2}]]_{r} in [[m_{1}]]_{r}[[m_{2}]]_{c}x

(where x does not appear free in m_{1}, m_{2})

[[{ℓ_{1} = m_{1}, …, ℓ_{n} = m_{n}}]]_{c} =^{def} λa. if a =_{A} ℓ_{1} then [[m_{1}]]_{c} ⋮ else if a =_{A} ℓ_{n} then [[m_{n}]]_{c} else ⋆

(where a does not appear free in m_{i})

[[{ℓ_{1} = m_{1}, …, ℓ_{n} = m_{n}}]]_{r} =^{def} let x_{1} = [[m_{1}]]_{r} in

⋮

let x_{n} = [[m_{n}]]_{r} in

λa. if a =_{A} ℓ_{1} then x_{1} ⋮ else if a =_{A} ℓ_{n} then x_{n} else ⋆

(where x_{i} does not appear free in m_{i})

[[π_{ℓ}(m)]]_{c} =^{def} [[m]]_{c}ℓ
[[π_{ℓ}(m)]]_{r} =^{def} [[m]]_{r}ℓ
[[m : σ]]_{c} =^{def} [[m]]_{c}
[[m : σ]]_{r} =^{def} [[m]]_{r}

Figure 3.6: Embedding Modules

Proposition 3.2 If ⊢_{ν} t in T and t ↦^{∗} t′ then ⊢_{ν} t′ in T. Proof

Direct from Corollary 4.4.

Corollary 3.3 (Type Preservation) If ⊢_{K} e : τ and [[e]] ↦^{∗} t then ⊢_{ν} t in [[τ]].

Another consequence of the soundness theorem is that the phase distinction [16, 46] is respected in λ^{K}: all type expressions converge and therefore types may be computed in a compile-time phase. This is expressed by Corollary 3.4:

Corollary 3.4 (Phase Distinction) If ⊢_{K} c : κ then there exists canonical t such that [[c]] ↦^{∗} t. Proof

For any well-formed λ^{K} kind κ, the embedded kind [[κ]] can easily be shown to be a total type. (Intuitively, every type is total unless it is constructed using the partial type constructor, which is not used in the embedding of kinds.) The conclusion follows directly.

3.5 Prospects for Extension

In order to embed λ^{K} into type theory, I abandoned impredicativity. This was because the standard semantics for Nuprl (discussed in Section 4.4) does not support it. However, Mendler [69] has developed a semantic model of Nuprl enhanced with recursive types and some impredicative polymorphism. In that type theory, it is entirely straightforward to handle second-order impredicativity. I have not used Mendler’s type theory in this thesis because its semantics is very complicated, and because it is not clear how easily it can be extended to support partial types. An alternative type theory to be explored is the Calculus of Constructions [29, 28], which also supplies impredicative features and could likely support the semantics discussed in this chapter.

Another important avenue for future work is to extend the semantics in this chapter to explain stateful computation. One promising device for doing this is to encode stateful computations as monads [82, 60], but this raises two difficulties. In order to encode references in monads, all expressions that may side-effect the store must take the store as an argument. The problem is how to assign a type to the store. Since side-effecting functions may be in the store themselves, the store must be typed using a recursive type, and since side-effecting expressions take the store as an argument, that recursive type will include negative occurrences of the variable of recursion. Mendler’s type theory may express recursive types with only positive occurrences, but to allow negative occurrences is an open problem.^{9}

Finally, a particularly compelling direction is to extend the semantics to account for objects, and that turns out to require only the same mechanisms discussed above. In a weak sense, this semantics can already support objects; the existential object encoding of Pierce and Turner [83] uses only constructs available within the type theory used here. Unfortunately, that encoding is not practical in a predicative system, because it involves quantification over the types of an object’s hidden instance variables. That quantification results in objects always belonging to a universe one level higher than their underlying code, which prevents such object from being first-class.^{10} However, in an impredicative type theory, Pierce and Turner’s object encoding can be used quite satisfactorily. In a type theory that additionally supplies recursive types (with negative occurrences), a variety of other object encodings become possible as well [15, 13, 3, 31, 14]. Alternatively, the object encoding of Hickey [50] works entirely within the existing Nuprl type theory and shows promise of being extendable to a practical object system.

3.6 Conclusions

I have shown how to give a type-theoretic semantics to an expressive programming calculus that supports modular and object-oriented features. This semantics makes it possible to use formal type-theoretic reasoning about programs and programming languages without informal embeddings and without sacrificing core expressiveness of the programming language.

Formal reasoning aside, embedding programming languages into type theory allows a researcher to bring the full power of type theory to bear on a programming problem. For example, in Crary [30] I use a type-theoretic interpretation to expose the relation of power kinds to a nonconstructive set type. Adjusting this interpretation to make the power kind constructive results in the proof-passing technique used to implement higher-order coercive subtyping in KML.

9See Birkedal and Harper [11] for a promising approach that may lead to a solution of this problem.

10However, in some contexts it is not necessary for objects to be first class. For example, Jackson [57] independently used an encoding essentially the same as Pierce and Turner’s to implement computational abstract algebra. In that context, algebras were rarely intermingled with the elements of an algebra, and when they were, an increase in the universe level was acceptable.

Furthermore, the simplicity of the semantics makes it attractive to use as a mathematical model similar in spirit, if not in detail, to the Scott-Strachey program [90]. This semantics works out so neatly because type theory provides built-in structure well-suited for analysis of programming. Most importantly, type theory provides structured data and an intrinsic notion of computation. Non-type-theoretic models of type theory can expose the “scaffolding” when one desires the details of how that structure may be implemented (Section 4.4).

As a theory of structured data and computation, type theory is itself a very expressive programming language. Practical programming languages are less expressive, but offer properties that foundational type theory does not, such as decidable type checking. I suggest that it is profitable to take type theory as a foundation for programming, and to view practical programming languages as tractable approximations of type theory. The semantics in this chapter illustrates how to formalize these approximations. This view not only helps to explain programming languages and their features, as I have done here, but also provides a greater insight into how we can bring more of the expressiveness of type theory into programming languages.

Foundational Type Theory

In this chapter I present in detail the foundational type theory of Nuprl. Broadly speaking, the intent of the Nuprl type theory is to provide a formal foundation for mathematics that is intrinsically computational. Like the simpler type theories that provide the superstructure for various programming languages (such as λ^{K}), Nuprl is a theory of structured data and computation. The essential difference between Nuprl (and other foundational type theories [68, 29, 42]) and the simpler type systems of ordinary programming languages is the expressiveness of the type system. Foundational type theories provide type structure rich enough to encode (almost) arbitrary logical and mathematical propositions.

This logical character of the Nuprl type system is not immediately evident at a first inspection. Instead, the constructs that make up Nuprl are those of a fairly simple programming language: data such as integers, functions, and pairs, and types for classifying such data. Those computational constructs are interpreted as logical statements using the propositions-as-types correspondence discussed in Section 4.1.3. This conflating of logic and data is one of the interesting aspects of type theory, and is in contrast to most other mathematical foundations (such as set theory), which draw sharp distinctions between objects and logical sentences.

4.1 Nuprl

The standard Nuprl type theory is presented in Constable et al. [19]. I present here a variant of the Nuprl type theory with a number of innovations. Chief among those innovations are the partial type mechanisms added in order to make it possible to reason about potentially nonterminating computations. In Section 4.3 I discuss some of the design decisions for my version of Nuprl.

4.1.1 Basic Types and Operators

At the core of the type theory are the basic data types summarized in Figure 4.1. Each of these data types have a collection of introduction operators, and a collection of analysis (or elimination) operators. Nuprl does not have distinct syntactic classes for types and terms; instead, types are ordinary terms that may be computed using the same operations for computing data. For example, if A and B are types then so is if b then A else B. The basic types of Nuprl are the following:

• Atom: The collection of introduction forms for Atom is the set of string literals. The sole elimination form for Atom is the equality test a =_{A} a′, which is equivalent to true when a

Type Formation  Introduction  Elimination
atoms  Atom  string literals  equality test (=_{A})
integers  ℤ  …, −1, 0, 1, 2, … assorted operations ⊕
booleans  𝔹  true, false  if e then e_{1} else e_{2}
disjoint union  T_{1} + T_{2}  inj_{1}(e)  case(e, x_{1}.e_{1}, x_{2}.e_{2})
inj_{2}(e)
product space Σx:T_{1}.T_{2} ⟨e_{1}, e_{2}⟩ π_{1}(e)
π_{2}(e)
function space Πx:T_{1}.T_{2} λx.e e_{1}e_{2}
very-dependent {f | x:T_{1} → T_{2}} λx.e  e_{1}e_{2}

function

unit  Unit  ⋆
void  Void
universe i  𝕌_{i}  type formation
(for i ≥ 1)  operators

Figure 4.1: Basic Nuprl Types

and a′ are equal atoms, and is equivalent to false when a and a′ are unequal atoms. (Note that Atom has considerably less structure than a type of strings; the essential properties of Atom are only a countably infinite set of introduction forms and an equality test.)

• Integer (ℤ): The collection of introduction forms for ℤ is the set of integer literals. The elimination forms for integers are boolean equality and inequality tests (=_{Z} and ≤_{Z}), and a collection of binary operations (ranged over by ⊕), such as plus, times, etc. For convenience, these operators are taken to be defined on all integer values and return zero in exceptional cases such as division by zero.

• Boolean (𝔹): The introduction forms for 𝔹 are true and false. Booleans are eliminated by the branching construct if b then e_{1} else e_{2}. (For simplicity, booleans will eventually be defined in terms of the unit and disjoint union types.)

• Disjoint Union (A + B): The introduction forms of A + B are inj_{1}(a) (when a is a term belonging to A) and inj (b) (when b is a term belonging to B). Disjoint unions are eliminated by the case analysis construct case(e, x_{1}.e_{1}, x_{2}.e_{2}).

• Dependent Product (Σx:A.B): The introduction forms of Σx:A.B are ⟨a, b⟩, when a belongs to A and b belongs to B[a/x]. Disjoint unions are eliminated by the projection constructs π_{1}(e) and π_{2}(e). As usual, when x does not appear free in B, I write Σx:A.B as the non-dependent product A × B. (This type is also known in the literature as a dependent sum, general sum or strong sum. I choose the term dependent product in order to emphasize the connection to the non-dependent product.)

• Dependent Function (Πx:A.B): The introduction form of Πx:A.B is λx.b, when b[a/x] belongs to B[a/x] for every a belonging to A. Functions are eliminated by the application construct e_{1}e_{2}. As usual, when x does not appear free in B, I write Πx:A.B as the non-dependent function type A → B.

• Very Dependent Function ({f | x:A → B}): The very dependent function type (due to Hickey [49]) has the same introduction form (λx.b) as the ordinary dependent function type, but allows the result type B to depend not only on the argument x but on the function itself. Thus, λx.b belongs to {f |x:A→B} when b[a/x] belongs to B[λx.b, a/f, x] for every a belonging to A.

To avoid circularity, in order for a very dependent function type {f | x:A.B} to be wellformed, we require that B only use f by applying it to elements of A that are less than x with respect to some well-founded order. As discussed in Section 3.3.3, the very dependent function type is sufficient to encode many of the other basic types discussed here, but it is convenient (especially in Chapter 5) to retain those other types as primitive.

• Unit: The type Unit has the introduction form ⋆ and no elimination forms. (For simplicity, Unit will eventually be defined in terms of the equality type.)

• Void: The type Void is empty and therefore has no introduction forms.

• Universe (𝕌_{i}): Each type expression is Nuprl is also an ordinary term, and is a member of a universe type. The universe 𝕌_{1} is the type containing all small types: those that can be built without using a universe. The universe 𝕌_{i+1} then contains all types that can be built using no universe higher than 𝕌_{i}. In particular, no universe is a member of itself; to include a single type that contained all types (including itself) would make the theory inconsistent [36, 27, 70, 52]. The introduction forms for a universe are the type formation operators. No elimination form for universes is provided, but this is only for simplicity; type analysis operations could easily be added, following Constable and Zlatin [20, 26].

4.1.2 Equality and Well-formedness

Central to the type theory are two equality relations, one for equality of types and another for equality of terms relative to a type. The type equality relation, written T_{1} = T_{2}, states that T_{1} and T_{2} are each well-formed types and are equal to each other. The term equality relation, written e_{1} = e_{2} ∈ T, states that T is a type and that e_{1} and e_{2} are members of T and are equal when considered as members of T. A specification of these two relations appears in Figure 4.5.

The reflexive forms of these relations are particularly important. Note that T = T exactly when T is a well-formed type, and that e = e ∈ T exactly when e is a member of T. These reflexive cases are abbreviated as T type and e ∈ T, respectively.

For example, Πx:A.B = Πx:A′.B′ if and only if A = A′ and B[a/x] = B′[a′/x] whenever a = a′ ∈ A; and λx.b = λx.b′ ∈ Πx:A.B if and only if Πx:A.B type and b[a/x] = b′[a′/x] ∈ B[a/x] whenever a = a′ ∈ A.

We will occasionally have need of a special degenerate type to serve as a supertype of all types:

• Top: For any terms e_{1} and e_{2}, e_{1} = e_{2} ∈ Top. (For simplicity, Top will eventually be defined in terms of the every type.)

Notation 4.1 I write ∀a ∈ A. P to mean ∀a. a ∈ A ⇒ P and ∀a = a′ ∈ A. P to mean ∀a, a′. a = a′ ∈ A ⇒ P.

4.1.3 Propositions as Types

In order to use type theory for mathematics, we need a way to make logical statements (propositions) within the formal theory. This is done using the propositions-as-types correspondence, also known as the Curry-Howard isomorphism [51]. In this correspondence, each logical sentence is interpreted as a type, with the understanding that that the sentence is true exactly when the corresponding type is inhabited by some term. That inhabitant is referred to as the witness of the proposition.

For example, the proposition False is interpreted as the uninhabited type Void. The other logical connectives are interpreted as follows:

True =^{def} Unit
P ∧ Q =^{def} P × Q
P ∨ Q =^{def} P + Q
P ⇒ Q =^{def} P → Q
P ⇔ Q =^{def} (P → Q) × (Q → P)
¬P =^{def} P → Void
∀x:T.P =^{def} Πx:T.P
∃x:T.P =^{def} Σx:T.P

Higher-order logic may be interpreted by this device as well. We may state a proposition quantified over all (small) predicates as ∀P:ℙ_{1}.Q. This is interpreted in the type theory by defining the type of propositions ℙ_{i} to be the universe 𝕌_{i}.

4.1.4 Equality, Subtyping and Inequality

Although the preceding is sufficient to encode propositional and higher-order predicate logic, we are short on atomic propositions. For example, we would like to be able to speak about equality within the logic, but none of the types discussed so far is able to express such a proposition. To handle this sort of atomic assertion, we add new types that are defined to be inhabited by the trivial term ⋆ when the proposition they represent is true, and empty otherwise:

• Equality (e_{1} = e_{2} in T): The type e_{1} = e_{2} in T is well-formed when e_{1} ∈ T and e_{2} ∈ T. If e_{1} = e_{2} ∈ T then the type e_{1} = e_{2} in T has the introduction form ⋆, if not then the type is empty (and is interpreted as a false proposition). Using the equality type, we may also define a membership type (denoted e in T) as e = e in T.

• Subtyping (T_{1} ⪯ T_{2}): The type T_{1} ⪯ T_{2} is well-formed when T_{1} and T_{2} are well formed types. If T_{1} is a subtype of T_{2} (i.e., e = e′ ∈ T_{2} whenever e = e′ ∈ T_{1}) then T_{1} ⪯ T_{2} has the introduction form ⋆, otherwise it is empty (and is interpreted as a false proposition).

• Inequality (n_{1} ≤ n_{2}): The type n_{1} ≤ n_{2} is well-formed when n_{1} ∈ ℤ and n_{2} ∈ ℤ and is inhabited (by ⋆) when the integer value of is n_{1} is less than or equal to that of n_{2}.

Be careful to note the distinction between e_{1} = e_{2} in T, a type (and a proposition) within the formal type theory, and e_{1} = e_{2} ∈ T, a metatheoretical statement outside the theory.

Also note that subtyping is defined as type inclusion: if T_{1} ⪯ T_{2} then the members of T_{1} are actual members of T_{2}. An alternative definition would be that T_{1} ⪯ T_{2} when exists a coercion from T_{1} to T_{2} [12]. This would provide an apparently more general notion of subtyping, since the identity function could always serve as a coercion from a type to another type that includes it. In its greatest generality, coercive subtyping may be represented by the proposition T_{1} ⇒ T_{2}, which states that there exists a function from T_{1} to T_{2}. Alternatively, if the coercion interpretation is restricted to use a predetermined collection of coercions, then the two notions of subtyping may be reconciled in a common type system [30]. This is done by building a new type system in which a type T_{2} contains all the members of every type T_{1} such that T_{1} is coercible to T_{2}. Then the equalities on such types are constructed so that coercion results are deemed equal to the corresponding coercion arguments, and thus coercion functions may formally be viewed as identity functions.

Negatability Consider the proposed proposition 3 in Atom. Certainly it is not true, but it is not false either: Since the well-formedness criterion for the equality type requires its equands to belong to the stated type, 3 in Atom is ill-formed and not a type at all. This seemingly innocuous phenomenon has considerable consequences; it means that the membership proposition is not negatable.

To understand why, consider the proposition e in T. If (metatheoretically speaking) e ∈ T then e in T is well-formed and true (i.e., inhabited). On the other hand, if e ∉ T then e in T is ill-formed and unusable. This does not mean that membership proposition is useless; on the contrary, it is quite often essential. However, the membership proposition is useless as the antecedent of an implication. As a simple example, the negation ¬(e in T) is either false or ill-formed, but never true and hence never useful. More generally, the implication e ∈ T ⇒ P is no easier to prove than P, because the well-formedness of the implication cannot be shown without proving the antecedent, rendering the antecedent useless.

The non-negatability phenomenon is not limited to the membership proposition. Consider the proposition ∀x:T_{1}. x in T_{2}. This proposition could be used to define the subtyping relation, but like the membership proposition, it is well-formed only when true. That is why subtyping is made a built-in type, because we would like to be able to prove theorems like A ⪯ B ⇒ ℤ → A ⪯ ℤ → B and ¬(𝕌_{2} ⪯ 𝕌_{1}). In Section 4.1.8 we will see a few more examples of primitive types included to provide negatability.

4.1.5 Constructivity

The interpretation of propositions as types is convenient, but it also restricts the proof techniques that may be used to prove such propositions. Consider the proposition XM = ∀P:ℙ_{1}. P ∨ ¬P. In classical logic, this proposition is certainly true: every proposition is either true or false (alternatively, every type is either inhabited or uninhabited). However, for XM to be valid in Nuprl, there must exist a function f that computes a member of P + ¬P for every (small) proposition P. This is certainly impossible, since P may express an undecidable proposition.

The Nuprl logic restricts proofs to using only principles of reasoning that are constructive (or intuitionistic [33]). This means that any proof of the existence of an object must show how to construct it, and any proof of a disjunction must show how to determine which case is true. In practice this means that proofs may not use the principle of the excluded middle (proposition XM above) or proof by contradiction. (Contradiction may, however, be used to prove theorems of the form ¬¬P.)

The dividend of this restriction is that proofs may be interpreted as programs. Suppose we prove ∀x:ℤ. ∃y:ℤ. P (x, y) constructively. In so doing, we have constructed a function inhabiting the corresponding type Πx:ℤ. Σy:ℤ. P (x, y); this function is the computational content of the proof. The function may be applied to any integer x to compute the desired integer y and a proof of P (x, y). More generally, whenever we prove the existence of objects, we may extract the computational content from that proof: a program to compute those objects. I discuss this process of extracting computational content from proofs further in Section 4.2.2. In Section 4.4.3 I discussed the controlled reintroduction of classical reasoning principles.

Propositions versus Booleans It is important to be clear on the distinction between the type ℙ_{i} of propositions and the type 𝔹 of booleans. Propositions are types that are inhabited when their intended meaning is true and are empty otherwise. Booleans are terms that compute to the value true when their intended meaning is true and to false otherwise. Decidable propositions may also be stated as boolean expressions; for example true, false, a =_{A} a′, n =_{Z} n′ and n ≤_{Z} n′ are boolean forms of the propositions True, False, a = a′ in Atom, n = n′ in ℤ and n ≤ n′. Undecidable propositions can never be stated as boolean expressions because a computation that resulted in such a boolean would solve the undecidable problem.

4.1.6 Constructing New Types using Logic

We may also use logical predicates to construct variants of other types, using two special type constructors (due to Constable [21]):

• The set type {x:T | P } is the subtype of T that contains all t ∈ T such that P [t/x] is true (i.e., inhabited).

• The quotient type xy:T//E[x, y] (when E[−, −] is an equivalence relation on T) is the supertype of T that coarsens the equality on T as follows: t_{1} = t_{2} ∈ xy:T//E if and only if t_{1}, t_{2} ∈ T and E[t_{1}, t_{2}/x, y] is true (i.e., inhabited).

We may use set and quotient types to define a number of important variants on existing types, such as the natural numbers, ℕ =^{def} {n : ℤ | 0 ≤ n}, and the integers modulo k, ℤ_{k} =^{def} mn:ℤ//((m − n) mod k = 0 in ℤ). A number of other derived forms appear in Figure 4.2.

An important point about set and quotient types is that they suppress computational content of their predicate. Given that n ∈ {x : ℤ | P [x]}, it is certainly the case that P [n] is inhabited by some term, but there is no way to gain access to that term. An an extreme example, consider the squashed proposition ↓P =^{def} {x : Unit | P }. Metatheoretically it is easy to see that P is inhabited exactly when ↓P is, and the implication P ⇒ ↓P can easily be shown valid. However, the converse ↓P ⇒ P is not valid, because it is not possible in general to produce a proof of a proposition simply from knowing it to be true. In order words, it is not generally possible to reconstruct suppressed computational content.

However, whenever the suppressed predicate is recursively enumerable, it is possible to reconstruct computational content. This covers many uses, including those few uses discussed above. On the other hand, it often is desirable to suppress computational content. For example, a proof of ∀x:ℤ. ∃y:ℤ. P (x, y) computes both an integer y and a witness to P (x, y), but it may be the case that so long as P (x, y) is true, the actual witness is not interesting. In such cases, returning the witness is cluttersome at best, and computationally expense at worst. It would then be more appropriate to prove the similar proposition ∀x:ℤ.{y : ℤ | P (x, y)}.

4.1.7 Computation

Intrinsic to the type theory is an operational semantics that defines how Nuprl terms compute. This computation system is call-by-name (in contrast to λ^{K}) as is defined by a small-step evaluation relation, t_{1} ↦ t_{2}, and a set of canonical forms to which terms evaluate. The computation system is defined to be the restriction to closed terms of the evaluation step relation

Unit =^{def} 0 = 0 in ℤ
𝔹 =^{def} Unit + Unit
true =^{def} inj_{1}(⋆)
false =^{def} inj_{2}(⋆)
if b then e_{1} else e_{2} =^{def} case(b, x.e_{1}, x.e_{2})  (where x is not free in e_{1}, e_{2})
e in T =^{def} e = e in T
T type =^{def} T ⪯ T
n < m =^{def} ¬(n ≥ m)
ℕ =^{def} {n : ℤ | 0 ≤ n}
ℤ^{+} =^{def} {n : ℤ | 0 < n}
ℤ_{k} =^{def} mn:ℤ//((m − n) mod k = 0 in ℤ)
ℚ =^{def} xy:(ℤ × ℤ^{+})//(π_{1}(x) × π_{2}(y) = π_{1}(y) × π_{2}(x) in ℤ)
↓P =^{def} {x : Unit | P }  (where x is not free in P)
t halts =^{def} t in! 𝔼
Top =^{def} xy:\overline{𝔼}//Unit

Figure 4.2: Derived Forms

given in Figure 4.3. Whether a term is canonical is governed by its outermost operator; the canonical forms of Nuprl are the type formation constructs and the introduction forms (i.e., the middle column of Figure 4.1). Two important properties of evaluation are that evaluation is deterministic and canonical forms are terminal:

Proposition 4.2 If t ↦ t_{1} and t ↦ t_{2} then t_{1} ≡ t_{2}. Moreover, if t is canonical then t ↛ t′ for any t′.

If t ↦^{∗} t′ and t′ is canonical then we say that t converges (abbreviated t↓) and t converges to t′ (abbreviated t ⇓ t′). Note that if t ⇓ t_{1} and t ⇓ t_{2} then t_{1} ≡ t_{2} and that if t is canonical then t ⇓ t.

The computation system may be used to define a computational equivalence relation (often known as applicative bisimulation) written t_{1} ∼ t_{2}. Computational equivalence will be formally defined in Chapter 5 (Definition 5.3). Intuitively, two terms are computationally equivalent if they both converge to canonical forms with the same outermost operator and the corresponding subterms are computationally equivalent, or if they both fail to converge. It may be shown (Lemma 5.5, using Howe’s method [53]) that computational equivalence includes beta-equivalence.

The equalities on types are constructed to observe computational equivalence;^{1} that is, if t ∈ T and t ∼ t′ then t = t′ ∈ T. This enables a powerful form of untyped reasoning called direct computation. When two terms are computationally equivalent (for example, a beta redex and contractum), they are indistinguishable by the type theory and may be freely exchanged for each other in any expression or proof:

Theorem 4.3 If T type and T ∼ T′ then T = T′. If t ∈ T and t ∼ t′ then t = t′ ∈ T.

1However, unlike the type system considered by Howe [53], equality in this type theory does not observe computational approximation (Section 5.2.1). This is because of the presence of partial types (Section 4.1.8).

For example, ⊥ approximates 1, but it is not the case that ⊥ = 1 in \overline{ℤ}.

e ↦_{s} e′

case(e, x_{1}.e_{1}, x_{2}.e_{2}) ↦_{s} case(e′, x_{1}.e_{1}, x_{2}.e_{2})

case(inj_{1}(e), x_{1}.e_{1}, x_{2}.e_{2}) ↦_{s} e_{1}[e/x_{1}]  case(inj_{2}(e), x_{1}.e_{1}, x_{2}.e_{2}) ↦_{s} e_{2}[e/x_{2}]
{e_{1} ↦_{s} e′_{1} \atop e_{1}e_{2} ↦_{s} e′_{1}e_{2}} (λx.e_{1})e_{2} ↦_{s} e_{1}[e_{2}/x] {e ↦_{s} e′ \atop π_{1}(e) ↦_{s} π_{1}(e′)} π_{1}(⟨e_{1}, e_{2}⟩) ↦_{s} e_{1}
{e ↦_{s} e′ \atop π_{2}(e) ↦_{s} π_{2}(e′)}  π_{2}(⟨e_{1}, e_{2}⟩) ↦_{s} e_{2}

e_{1} ↦_{s} e′_{1} e_{1} (=_{A}/=_{Z}/≤_{Z}) e_{2} ↦_{s} e′_{1} (=_{A}/=_{Z}/≤_{Z}) e_{2} e_{1} is a string/integer literal e_{2} ↦_{s} e′_{2}

e_{1} (=_{A}/=_{Z}/≤_{Z}) e_{2} ↦_{s} e_{1} (=_{A}/=_{Z}/≤_{Z}) e′_{2}

e_{1} and e_{2} are string/integer literals, i = 1 iff e_{1} ≡ e_{2} e_{1} (=_{A}/=_{Z}) e_{2} ↦_{s} inj_{i}(⋆)

e_{1} and e_{2} are integer literals, i = 1 iff e_{1} ≤ e_{2}
e_{1} ≤_{Z} e_{1} ↦ inj_{i}(⋆)

e_{1} ↦_{s} e′_{1}

let x = e_{1} in e_{2} ↦_{s} let x = e′_{1} in e_{2}

e_{1} is canonical let x = e_{1} in e_{2} ↦_{s} e_{2}[e_{1}/x]

e_{1} ↦_{s} e′_{1} e_{1} ⊕ e_{2} ↦_{s} e′_{1} ⊕ e_{2} e1 is an integer literal e_{2} ↦_{s} e′_{2}

e_{1} ⊕ e_{2} ↦_{s} e_{1} ⊕ e′_{2}

e1 and e2 are integer literals e_{1} ⊕ e_{2} = e
e_{1} ⊕ e_{2} ↦_{s} e

fix(e) ↦_{s} e fix(e)

Figure 4.3: Operational Semantics for Nuprl This theorem is codified in the type theory by the direct computation rules, Rules 8 through 15 (Appendix B). Since computational equivalence is undecidable (and since we wish to be able to effectively determine whether an instance of a rule is valid), the direct computation rules approximate computational equivalence with the auxiliary judgement ⊢_{ν} t ∼ t′ (where t and t′ are possibly open terms).

A simple application of Theorem 4.3 and direct computation is type preservation:

Corollary 4.4

• (Semantic type preservation) If t ∈ T and t ↦ t′ then t′ ∈ T.

• (Syntactic type preservation) If H ⊢_{ν} t in T and t ↦ t′ then H ⊢_{ν} t′ in T.

Proof

If t ↦ t′ then t ∼ t′ (Proposition 5.4) and ⊢_{ν} t ∼ t′ (Rule 14). Semantic preservation follows directly by Theorem 4.3 and syntactic preservation follows by application of Rules 8 and 11.

4.1.8 Partial Types

Of particular interest in the computation system is the operator fix, which allows recursive computation and is evaluated by the rule fix(f) ↦ f(fix(f)).^{2} The primary use of fix is to define recursive functions. Unfortunately, in the standard Nuprl type theory, recursion is not as general tool as it is in most programming languages. All of the basic types of Nuprl are total (that is, they contain only convergent terms), but in general there is no guarantee that a recursive function converges. Thus, a recursive function (generally, a recursively computed object) cannot be given a type unless it can be shown to converge, and it cannot be used without giving it a type. However, it is not always convenient (or possible) to prove that recursive functions terminate, and furthermore, it is sometimes interesting to reason about functions that are known not to terminate for all inputs.

In order to be able to assign types to partial functions and nonconvergent objects, we add a new type constructor:

• Partial type (\overline{T}): The partial type \overline{T} (due to Constable and Smith [24, 25, 92]) is like a lifted version of T: It contains all members of T, plus all nonconvergent terms. Terms are equal in \overline{T} if they have the same convergence behavior, and if they are equal in T when they converge. That is, t = t′ ∈ \overline{T} if and only if t↓ ⇔ t′↓ and t↓ ⇒ t = t′ ∈ T.

With this type available, we may express the type of partial functions from A to B as A → \overline{B}. It will also prove convenient to have a type of all convergent terms:

• Every (𝔼): For any convergent terms e_{1} and e_{2}, e_{1} = e_{2} ∈ 𝔼.

In order to reason about termination in the logic, we add a new type to represent the atomic proposition of convergence using the same device that we used to add equality, subtyping and inequality types:

• Convergence (t in! T): The type t in! T is well-formed when t ∈ \overline{T} and is inhabited (by ⋆) exactly when t↓.

2The use of a fix operator greatly simplifies some of the results in this thesis (particularly the proof of Theorem 5.9), but it could in principle be eliminated and replaced with the Y combinator.

Note that t in! T is an inhabited type exactly when t in T is. A distinct convergence proposition is needed in order to provide negatability, which is essential for any nontrivial termination reasoning. Observe that t in! T is well-formed whenever t ∈ \overline{T}, but t ∈ T is well-formed only when it is true.

Constable and Smith [25, 92, 91] use an untyped convergence assertion in their type systems. In this type theory I instead use a typed convergence assertion (as in Constable and Smith [24]). However, an untyped convergence assertion can be defined as t halts = t in! 𝔼. (This is the primary motivation for including the 𝔼 type.) Unfortunately, as we will see in Section 4.3.1, in the presence of equality the untyped version is rarely any more useful than the typed version.

Partial Type Formation The convergence type raises a somewhat surprising issue about the formation of partial types. The type equality rule for convergence types states that t in! T and t′ in! T are equal when t = t′ ∈ \overline{T}, and equal types should always have the same membership. Therefore, \overline{T} should never equate convergent and non-convergent terms. However, we desire that T be a subtype of \overline{T} (i.e., if t = t′ ∈ T then t = t′ ∈ \overline{T}), so T should never equate convergent and non-convergent terms either. Since many terms (Top for example) do equate terms with different convergence behavior, we must add this as a restriction to the well-formedness of partial types.

To not equate convergent and non-convergent terms is an awkward condition to maintain, so instead my theory imposes the stronger condition that for \overline{T} to be well-formed, T must be total. It follows trivially that a total type cannot equal convergent and non-convergent terms. The obvious definition of totality, ∀x:T. x in! T, turns out to be non-negatable (since it depends upon the well-formedness of \overline{T}) so I add totality as another primitive type:

• Totality (T total): The type T total is well-formed when T type and is inhabited (by ⋆) exactly when T contains only terms that converge.

The Fixpoint Principle With partial types in the type theory, recursive functions can be typed using the fixpoint principle:

e in \overline{T} → \overline{T} ⇒ fix(e) in \overline{T}

The use of the fixpoint principle was illustrated in Section 3.3.2. However, the fixpoint principle is not valid for every type; it is only valid for types that are admissible. Admissibility is defined formally in Chapter 5 (Definition 5.13). Intuitively, a type is admissible if it contains a recursive term whenever it contains a cofinite set of approximations to that term.

This issue does not arise in most ordinary programming languages because most type systems are not rich enough to allow the expression of inadmissible types. Nuprl is expressive enough to construct inadmissible types, but nevertheless most types are still admissible. Chapter 5 discusses admissibility in detail, and presents a number of techniques for showing types to be admissible.

Admissibility is introduced into the logic as another atomic proposition:

• Admissibility (T admiss): The type T admiss is well-formed when T type and is inhabited (by ⋆) exactly when T is admissible.

This completes the set of primitive Nuprl types (except for four additional atomic propositions introduced in Chapter 5). The list is summarized in Figure 4.4.

void  Void
atom  Atom
integer  ℤ
disjoint union  A + B
product space  Σx:A.B
function space  Πx:A.B
very-dependent function {f | x:A → B}
partial type \overline{A}
universe  𝕌_{i}

equality  e_{1} = e_{2} in A
subtyping  A ⪯ B
inequality  n_{1} ≤ n_{2}
convergence e in! A
totality  A total
admissibility A admiss
set  {x : A | P }
quotient  xy:A//P
every  𝔼

Figure 4.4: Primitive Nuprl Types

Sequencing The final construct in the partial type theory is the sequencing construct let x = e_{1} in e_{2}. This expression evaluates e_{1} to canonical form (say v) then evaluates e_{2}[v/x]. Hence, the sequence term diverges if either e_{1} or e_{2} does. Since e_{2} is only evaluated if e_{1} converges, the e_{2} portion of the sequencing term may be typed under the assumption that x has a total type (the syntax for Nuprl sequents is formally defined in the next section):

H ⊢_{ν} e_{1} in \overline{T} H; x:T ⊢_{ν} e_{2} in \overline{T′} H ⊢_{ν} \overline{T′} type
H ⊢_{ν} let x = e_{1} in e_{2} in \overline{T′}

As an aside, if \overline{T} is reinterpreted as a monad type M(T), then this is precisely the typing rule for the bind operator in monad systems [74, 95, 94]. A monad system would also have a unit operator to coerce T to M(T); in this theory that is taken care of by subsumption, since T ⪯ \overline{T}.

One may extend the theory to a full type theory of monads by dispensing with T ⪯ \overline{T}, adding an explicit unit operator and adding rules comprising the monadic equality laws. This would provide a powerful mechanism for abstractly managing effects in general [34], not just the nontermination effects managed by the partial types. I have not done so in this type theory, preferring instead to keep the type theory closely tied to its operational semantics, thereby providing as much structure as possible at the expense of abstraction (recall Section 3.2.1).

4.2 Sequents and Proof

The Nuprl inference system has one judgement form, written as the sequent:

x_{1}:T_{1}; …; x_{n}:T_{n} ⊢_{ν} C ◁ t

This may be read informally as “t is a member of C when x_{1}, …, x_{n} are members of T_{1}, …, T_{n}.” A more precise interpretation is discussed in Section 4.2.1 and a formal semantics is defined in Section 4.4.2. The bindings (x_{1}:T_{1}; …; x_{n}:T_{n}) to the left of the turnstile are called hypotheses, C is called the conclusion and t is called the witness.

The inference rules for deriving judgements of this form are listed in Appendix B. By way of example, Rule 21 for applying subtyping amidst the hypothesis list is:

\begin{matrix}H; x : T′; J ⊢_{ν} C ◁ s & H ⊢_{ν} T ⪯ T′ \\ H; x : T; J ⊢_{ν} C ◁ s \end{matrix}

Note that in this rule the second subgoal is a subtyping proposition, which is computationally uninteresting (it contains only terms that evaluate to ⋆). Since that proposition is computationally uninteresting, the statement of the rule suppresses the witness. In many rules the consequent also is computationally uninteresting and the witness is suppressed in such rules as well. In those rules, the witness should be taken to be ⋆. An example of such a rule is Rule 21 for applying subtyping in an equality assertion:

\begin{matrix}H ⊢_{ν} t_{1} = t_{2} in T′ & H ⊢_{ν} T′ ⪯ T \\ H ⊢_{ν} t_{1} = t_{2} in T \end{matrix}

Implicitly, this stands for the rule:

\begin{matrix}H ⊢_{ν} t_{1} = t_{2} in T′ ◁ t & H ⊢_{ν} T′ ⪯ T ◁ t′ \\ H ⊢_{ν} t_{1} = t_{2} in T ◁ ⋆ \end{matrix}

4.2.1 Functionality

A sequent x_{1}:T_{1}; …; x_{n}:T_{n} ⊢_{ν} C ◁ t asserts not only membership of the witness t in the conclusion type C, it also asserts that the conclusion and witness are functional over the hypothesis variables. That is, if t⃗ = t_{1}, …, t_{n} and t⃗′ = t′_{1}, …, t′_{n} are equal substitutions for x_{1}:T_{1}, …, x_{n}:T_{n} (in a manner that will be made precise in Section 4.4.2) then C[t⃗/x⃗] = C[t⃗′/x⃗] and t[t⃗/x⃗] = t[t⃗′/x⃗] ∈ C[t⃗/x⃗]. To see why this interpretation is necessary, consider the rule:

\begin{matrix}H ⊢_{ν} A type & H; x : A ⊢_{ν} B ◁ b \\ H ⊢_{ν} A → B ◁ λx.b \end{matrix}

The consequent states that λx.b is a function over A; that is, if a = a′ ∈ A then b[a/x] = b[a′/x] ∈ B. Under a weaker, membership only, interpretation of sequents, the second subgoal is sufficient to conclude that b[a/x] ∈ B and b[a′/x] ∈ B, but cannot guarantee equality. A functional interpretation of sequents (where sequents are seen as functions from hypotheses to the conclusion) is necessary to validate the many rules of this form.^{3}

4.2.2 Extract-Style Proof

In order to prove some proposition P, the propositions-as-types correspondence dictates that one prove a sequent of the form ⊢_{ν} P ◁ t. However, it is typically the case that one is only interested in proving the truth (inhabitation) of a proposition, and is not concerned that witness be a particular term. Consequently, in the Nuprl proof development system [19, 56], a user is presented with a sequent that omits the “◁ t” witness term, even in cases when conclusion is computationally interesting. The rules are then stated in a manner that makes it possible to extract the witness from the proof. This is called extract-style proof. For example, consider again the rule:

\begin{matrix}H ⊢_{ν} A type & H; x : A ⊢_{ν} B ◁ b \\ H ⊢_{ν} A → B ◁ λx.b \end{matrix}

In extract-style proof, this rule is applied to a sequent of the form H ⊢_{νe} P ⇒ Q to produce subgoals H ⊢_{νe} P type and H; x : P ⊢_{νe} Q. From the proof of the second subgoal is extracted a witness b, possibly containing free occurrences of the variable x, and that witness is used to construct the extract, λx.b, of the original sequent. In fact, since the variable x is only used in the extract, it may be made invisible to the user as well, resulting in the simple subgoal H; P ⊢_{νe} Q.

3In some alternative semantics for type theory (such as Howe’s set theoretic semantics [54]), equality in a type may be collapsed to actual object equality, trivially making the membership and functionality interpretations the same. Extending the semantics of Howe to account for all the devices in this type theory is an open problem, but one that shows some promise of feasibility.

For extract-style proof to be possible, all the rules in the system must be of the proper form. Specifically, the rules must construct extracts in a bottom-up manner, or, in other words, the applicability of a rule must not depend on the extract, since the extract cannot be known at the time the rule is applied. However, the phrasing of the Nuprl proof rules that I state in Appendix B, is devised in the interest of making the type theory as simple as possible, not supporting extract-style proof, so some rules are not of the proper form. To support extract-style proof, those rules must be replaced with extract-compatible rules. Fortunately, the necessary rules are all derivable from the existing rules, so it is possible to support extract-style proof without any additional metatheoretical justification.

The necessary condition for extract-compatibility is best understood by examining some rules that violate it and seeing how they may be rewritten to follow it:

Extract and Membership The function type inhabitation rule discussed above is not among the basic rules of the inference system. Rather it is derivable from them the equality rule on function types:

H ⊢_{ν} A type {H; x : A ⊢_{ν} B ◁ b \atop H; x : A ⊢_{ν} b in B} (5)

H ⊢_{ν} λx.b in A → B (60)

H ⊢_{ν} A → B ◁ λx.b (4)

We gain considerable economy in the derivation rules by leaving out such inhabitation rules and allowing them to be derived from the others, but in an extract-style system they must be built-in instead. This is because Rule 5 is not appropriate for extract-style proof:

{H ⊢_{ν} C ◁ t \atop H ⊢_{ν} t in C}

For this rule to be applicable, the extract t of the subproof must be the same term t that is specified in the membership proposition in the consequent, and there is no guarantee that it will. Another way of looking at this problem is that the rule with witnesses stripped out is unsound; it does not follow from the inhabitation of C that any particular term t belongs to C:

{H ⊢_{νe} C \atop H ⊢_{νe} t in C} (wrong)

In an extract-style proof system, Rule 5 must be omitted and replaced by an inhabitation rule to mirror each basic equality rule (e.g., 47, 48, 53, 60). However, note that the converse rule, Rule 4, does not pose problems for extract-style proof.

Hypothesis Elimination Another class of rules that impose a restriction on the extract term is the hypothesis elimination rules. The hypothesis elimination rule for products requires (Rule 56) that all free occurrences of x and y in the extract term must appear as the pair ⟨x, y⟩:

{H; x : A; y : B; J[⟨x, y⟩/z] ⊢_{ν} C[⟨x, y⟩/z] ◁ s[⟨x, y⟩/z] \atop H; z : Σx:A.B; J ⊢_{ν} C ◁ s} (x, y ∉ J, C, s)

To make this rule suitable for extract-style proof, it must be rephrased as the derivable rule:

{H; x : A; y : B; J[⟨x, y⟩/z] ⊢_{ν} C[⟨x, y⟩/z] ◁ s \atop H; z : Σx:A.B; J ⊢_{ν} C ◁ s[π_{1}(z), π_{2}(z)/x, y]}

Similarly, the hypothesis elimination rule for disjoint unions,

H; y : A; J[inj_{1}(y)/x] ⊢_{ν} C[inj_{1}(y)/x] ◁ s[inj_{1}(y)/x]
H; y : B; J[inj_{2}(y)/x] ⊢_{ν} C[inj_{2}(y)/x] ◁ s[inj_{2}(y)/x]

H; x : A + B; J ⊢_{ν} C ◁ s (y ∉ J, C, s)

must be rephrased as the derivable rule:

H; y : A; J[inj_{1}(y)/x] ⊢_{ν} C[inj_{1}(y)/x] ◁ s_{1}
H; y : B; J[inj_{2}(y)/x] ⊢_{ν} C[inj_{2}(y)/x] ◁ s_{2}
H; x : A + B; J ⊢_{ν} C ◁ case(x, y.s_{1}, y.s_{2})

In contrast to these two rules, the hypothesis elimination rules for types with “uninteresting” memberships, such as the equality types (Rule 99), do not pose a problem:

{H; x : (t = t′ in T); J[⋆/x] ⊢_{ν} C[⋆/x] ◁ s[⋆/x] \atop H; x : (t = t′ in T); J ⊢_{ν} C ◁ s}

In these rules it is not a problem for ⋆ terms to propagate upwards, so such rules may be applied simply by assuming that x does not appear free in s.

Hidden Variables A special case of hypothesis elimination that poses particular difficulties is that of set and quotient types. The hypothesis elimination rule for set types (Rule 86) is:

{H; x : A; y : B; J ⊢_{ν} C ◁ s \atop H; x : \{x : A | B\}; J ⊢_{ν} C ◁ s} (y ∉ J, C, s)

As before, y is prohibited from appearing free in the extract term s, but in this case the rule cannot be rephrased to make the problem disappear. The essential problem is that y represents computational content that is not available, so the extract cannot depend upon it in any way. To enforce this restriction, we must introduce a new mechanism: When the rule is applied, the variable y is marked as hidden, establishing the invariant that it may not appear free in the extract:

{H; x : A; [y : B]; J ⊢_{νe} C \atop H; x : \{x : A | B\}; J ⊢_{νe} C}

This invariant is preserved by the inference system, and disallows the application of any rule that would use that variable. However, when descending into subgoals that do not contribute to the extract all hiding annotations may be removed.

It is important to note that this hidden variable mechanism is only a practical syntactic device, and has no metatheoretical significance. The sole metatheoretical issue is addressed in Rule 86 by the side condition that y not appear free in the extract.

4.3 Issues in Partial Object Type Theory

The type theory presented here joins together the expressiveness of the standard Nuprl type theory, which does not support reasoning about partial objects (that is, nonconvergent terms), and the type theory of Smith [92, 91], which addresses partial objects but does not support equality. This theory also enhances Smith’s theory by a considerably wider class of types that are admissible for fixpoint induction (discussed at length in Chapter 5). Here I discuss some issues arising in the design and application of a partial object type theory, particularly those resulting from the combination of equality and partial objects.

4.3.1 Partial Computation

Given two terms a and b, each of which belong to \overline{ℤ}, it is intuitively clear that the sum a + b should also belong to \overline{ℤ}. However, the rules for integer expressions deal with convergent terms:

\begin{matrix}H ⊢_{ν} a in ℤ & H ⊢_{ν} b in ℤ \\ H ⊢_{ν} a + b in ℤ \end{matrix}

An informal argument for the desired membership is easy: To show that a + b in \overline{ℤ}, suppose that (a + b) halts and show a + b in ℤ. If (a + b) halts, then a halts and b halts. Consequently a in ℤ and b in ℤ and therefore a + b in ℤ by the rule.

Unfortunately, formalizing this argument as a derivation runs into a problem; to show the desired membership we incur the additional obligation of showing that the supposition (a + b) halts is well-formed, as in the derivation below, and the eventual subgoal a + b in \overline{𝔼} is no easier to prove than the original goal a + b in \overline{ℤ}.

H ⊢_{ν} a + b in \overline{𝔼}
H ⊢_{ν} (a + b) halts type

(108)

⋮ .

H ⊢_{ν} (a + b) halts ⇔ (a + b) halts H; x : (a + b) halts ⊢_{ν} a + b in ℤ

H ⊢_{ν} a + b in \overline{ℤ} (71)

This difficulty does not reveal a defect in the logic. The problem is that in the presence of equality, as discussed in Section 4.2.1, a sequent says much more than its naive reading; it asserts the functionality of the conclusion over equal substitutions for the hypotheses. The informal argument at the top is perfectly valid as a metatheoretical argument for closed terms, but it does not extend to sequents.

However, it certainly would be a defect in the logic if goals of this form were unprovable. This could be settled by fiat, by simply adding new partial typing rules for each operator, but we would rather avoid such a heavy-handed solution. I present a more palatable solution in the next section.

4.3.2 Computation in Active Positions

Consider the monad-style variation of a + b obtained by first computing the summands to canonical form and them summing their values: let x = a in let y = b in x + y. This is easily shown to belong to \overline{ℤ}:

H ⊢_{ν} a in \overline{ℤ}

H; x : ℤ ⊢_{ν} b in \overline{ℤ} \begin{matrix}H; x : ℤ; y : ℤ ⊢_{ν} x + y in ℤ & (44, 1) \\ H; x : ℤ; y : ℤ ⊢_{ν} x + y in \overline{ℤ} \end{matrix} ; (70)

H; x : ℤ ⊢_{ν} (let y = b in x + y) in \overline{ℤ} (81)

H ⊢_{ν} (let x = a in let y = b in x + y) in \overline{ℤ}

(81)

The desired result then follows by direct computation if it can be shown that ⊢_{ν} a+b ∼ (let x = a in let y = b in x + y). Of course, in general t_{2}[t_{1}/x] is not equivalent to let x = t_{1} in t_{2}, because the latter necessarily evaluates t_{1} but the former might not, which would lead to different convergence behavior if t_{1} is divergent. However, in this case the term a + b does evaluate a and b before converging.

The general principle at work here is that x and y are both in active positions in the term x + y. An active position of a term is one that must be evaluated before the term can converge. Canonical terms have no active positions, since they have already converged. The active positions of non-canonical terms are defined as follows:

Definition 4.5 A term e appears actively in t if:

• t ≡ e, or

• t ≡ case(t_{1}, x.t_{2}, x.t_{3}) and e appears actively in t_{1}, or

• t ≡ t_{1}t_{2} and e appears actively in t_{1}, or

• t ≡ π_{i}(t′) and e appears actively in t′, or

• t ≡ t_{1} =_{A} t_{2} or t ≡ t_{1} =_{Z} t_{2} or t ≡ t_{1} ≤_{Z} t_{2} and e appears actively in t_{1} or t_{2}, or

• t ≡ let x = t_{1} in t_{2} and e appears actively in t_{1} or t_{2}, or

• t ≡ t_{1} ⊕ t_{2} and e appears actively in t_{1} or t_{2}, or

• t ≡ fix(t′) and e appears actively in t′

Lemma 4.6 If x appears actively in t then t[e/x] ∼ let x = e in t.

This lemma is put into practice in the type theory as Rule 15:

x appears actively in t_{2} ⊢_{ν} t_{2}[t_{1}/x] ∼ let x = t_{1} in t_{2}

Another application of this device is for computational inducement. Observe that evaluating a + b induces the evaluation of a and b, so if a + b converges then so do a and b. This can be proven using active positions:

H ⊢_{ν} a + b in! ℤ ⊢_{ν} (let x = a in x + b) ∼ a + b (15, 11)
H ⊢_{ν} (let x = a in x + b) in! ℤ

(8)

H ⊢_{ν} a in \overline{ℤ}

H ⊢_{ν} a in! ℤ (80)

4.3.3 Fixpoint Induction

The principal device for reasoning about programs in the LCF system [38] was fixpoint induction. Informally, fixpoint induction says that if an “admissible” predicate holds for divergent terms and it is preserved by f, then it holds for the fixpoint of f. In notation: if P [⊥] and if P [x] ⇒ P [f(x)] then P [fix(f)].

Using the fixpoint principle in our type theory, we may derive a similar induction rule:

H; x : \overline{T}; y : (x in! T ⇒ P) ⊢_{ν} P
H; x : \overline{T}; y : P; z : (fx in! T) ⊢_{ν} P [fx/x]
H ⊢_{ν} f in \overline{T} → \overline{T}
H; x : \overline{T} ⊢_{ν} P type
H ⊢_{ν} T total
H ⊢_{ν} T admiss
H ⊢_{ν} P admiss (x : T)
H; x : \overline{T}; y : ↓P ⊢_{ν} P ◁ e
H ⊢_{ν} P [fix(f)/x] ◁ e[fix(f), ⋆/x, y]

This rule looks considerably more complicated than the informal statement, so let us examine it in detail. The first subgoal,

H; x : \overline{T}; y : (x in! T ⇒ P) ⊢_{ν} P

corresponds to the base case, P [⊥]. If one accepts classical reasoning principles, in particular the proposition (x in! T) ∨ ¬(x in! T), then this goal may be proven from the simpler goal H ⊢_{ν} P [⊥/x]. In Section 4.4.3, I discuss how classical reasoning principles may be justified semantically if one takes a classical view of the metatheory. However, it is usually the case that the given subgoal can be proven whenever H ⊢_{ν} P [⊥/x] can, so classical methods are usually unnecessary.

The second subgoal,

H; x : \overline{T}; y : P; z : (fx in! T) ⊢_{ν} P [fx/x]

corresponds to the inductive case, P [x] ⇒ P [f(x)]. The z hypothesis indicates that we are not in the base case (i.e., f(x) ≠ ⊥), and may be dropped, if desired, to bring the goal more in line with the informal statement.

The next three subgoals are basic well-formedness conditions. The sixth subgoal, H ⊢_{ν} T admiss, is also a well-formedness condition, which is needed to take the fixpoint of f. The seventh subgoal, H ⊢_{ν} P admiss(x : T), is the requirement on the admissibility of the predicate. (More on this in Section 5.3.1.)

The final subgoal is the most curious:

H; x : \overline{T}; y : ↓P ⊢_{ν} P ◁ e

This goal stems from the uniformity condition for admissibility of set types (Section 5.3.3), but we may give an informal explanation directly in the context of fixpoint induction. Suppose v_{0} and g are the computational content of the base case and inductive case, respectively. For simplicity, assume that v_{0} ∈ P [⊥/x] and g ∈ P [t/x] → P [f(t)/x] (for all t ∈ \overline{T}). Then we may build a sequence of P inhabitants for each of the finite approximations of fix(f): Let v_{i+1} = g(v_{i}) so v_{0} ∈ P [⊥/x], v_{1} ∈ P [f(⊥)/x], v_{2} ∈ P [f(f(⊥))/x], etc. We need a limit term to inhabit the proposition P [fix(f)/x]. However, the sequence terms v_{0}, v_{1}, v_{2}, … are unrelated by computational approximation (Section 5.2.1) and do not in general approach any limit.

The final subgoal provides a term e that allows us to build a uniform sequence of inhabitants that will approach a limit. Since the sequence v_{0}, v_{1}, v_{2}, … inhabits the various P propositions, the sequence e[⊥/x], e[f(⊥)/x], e[f(f(⊥))/x], … also inhabits the various P propositions, and the latter is guaranteed to approach a limit.

Lemma 4.7 The fixpoint induction rule is derivable.

Proof

The full derivation of the rule is tedious, so I sketch the argument. Let T′ = {x : T | P }. The key points are to show \overline{T′} ⪯ \{x : \overline{T} | P \} and to show that f in \overline{T′} → \overline{T′}. From the latter, and since T′ is admissible (Rule 143 and subgoals 6, 7 and 8), we may conclude that fix(f) in \overline{T′}. Thus, by the first point, fix(f) in \{x : \overline{T} | P \}. We may then use subgoal 8 to reconstruct the suppressed computational content of P [fix(f)/x].

To show \overline{T′} ⪯ \{x : \overline{T} | P \} uses the base case. Suppose x in \overline{T′}. We wish to show that x in \overline{T} and P is inhabited. The former is easy, since T′ ⪯ T. To show the latter, suppose x halts (i.e., x in! T). Then x in T′, so P is inhabited. Hence x in! T ⇒ P. By the base case subgoal, P is inhabited.

To show f in \overline{T′} → \overline{T′} uses the inductive case. Suppose x in \overline{T′}. We wish to show that fx in \overline{T′}. Suppose fx halts (i.e., fx in! T). Then we wish to show that fx in T′; that is, that fx in T and P [fx/x] is inhabited. The former follows trivially from fx in! T. To show the latter, observe that x in \{x : \overline{T} | P \} (since \overline{T′} ⪯ \{x : \overline{T} | P \}). Consequently P is inhabited. By the inductive case subgoal, P [fx/x] is inhabited. □

Two somewhat similar results to Lemma 4.7 were proven by Smith [92, 91]. Smith’s results differed in that one used classical reasoning principles and was based on the then-conjectural admissibility of set types, and the other required the witnesses of the base and inductive cases to have particular forms.

4.3.4 Computational Induction

Smith’s type theory also provided another principle, computational induction, for reasoning about partial objects. The idea to computational induction is that when a term converges, it induces a well-founded order on its computation’s intermediate values. Computational induction is generally useful for reasoning about programs since it corresponds to the intuitive mechanism of reasoning about recursive programs by preconditions and postconditions, but it is particularly useful for proving partial correctness of programs. Other induction principles are inadequate for such proofs; if one could find a well-founded order on the inputs of a recursive function, one could show total correctness instead.

Unfortunately, computation is an intrinsically intensional property; that is, it requires reasoning about the structure of terms as well as about their result value. In contrast, my type theory is extensional; terms with the same result value are indistinguishable. Consequently, it is trivially true that P [t_{2}] ⇒ P [t_{1}] when t_{1} ↦ t_{2}, and one may not draw any inductive conclusions from the fact. Smith’s type theory was able to support computational induction because it supported no notion of equality or functionality.

In order to support computational induction, we need to add support for intensional reasoning about terms. We may do this by adding to the type theory representations for all terms, and by adding rules for reasoning intensionally about those representations and for linking those representations back to the terms they represent. Mechanisms for accomplishing this are discussed in detail in Constable and Crary [23].

4.3.5 Other Contributions

The version of the Nuprl type theory presented in this chapter makes a few minor contributions other than those contributions relating to partial types:

• By adding a primitive subtyping assertion, subtyping becomes negatable (recall Section 4.1.4). This makes it possible to prove theorems that depend upon subtyping conditions. More importantly, however, it makes it possible to define the power kind as P_{i}(T) = {S : 𝕌_{i} | S ⪯ T } (recall Section 3.3.3). Without a negatable subtyping assertion, this set type is ill-formed.

• This type theory adds rules for a typehood assertion, T type, which may be defined either as λx.x in T → T or as T ⪯ T. I use the latter definition, in order to share some rules with the subtyping assertion. Using a typehood assertion streamlines dealings with well-formedness subgoals, such as the first subgoal of:

H ⊢_{ν} A type H; x : A ⊢_{ν} b in B
H ⊢_{ν} λx.b in Πx:A.B

In the standard Nuprl theory, which does not use typehood assertions, the well-formedness subgoal is stated as membership in some universe: A in 𝕌_{i}. However, to prove such a subgoal requires the determination of which universe 𝕌_{i} the type A belongs to. This is often inconvenient, and is sometimes impossible. For example, sometimes typehood can only be shown using an inhabitation rule (Rule 19):

{H ⊢_{ν} t = t′ in T \atop H ⊢_{ν} T type}

This rule could not be stated in the standard theory, using universe membership, because it would need to conclude that T was a member of some unknown universe 𝕌_{i}, and there is no facility for existentially quantifying universe indices.

The use of typehood assertions is also useful in that it makes the type theory extensible to systems where some types do not belong to any universe. For example, suppose the computation system were augmented with a noncanonical operator U(−) with the evaluation rule U(n) ↦_{s} 𝕌_{n} (for integer literals n). Then Σx:ℤ. U(x) would be a valid type, but since U(x) spans the universes, the type would be too large to belong to any single universe.

• The inclusion of an inhabitation-to-membership rule (Rule 5) makes it possible to derive the type inhabitation rules from the rules governing equality on types. This cuts the number of rules nearly in half. As discussed in Section 4.2.2, an extract-style proof development system must reintroduce those rules, but they may be reintroduced as derived rules without additional metatheoretic justification.

4.4 Semantics

The remaining task is to show that the type theory presented here is consistent. As usual, I do this by showing it sound with respect to a semantics. The development of the semantics is divided into two parts; I begin by giving a semantics for types and then extend that semantics to cover judgements as well. I give this semantics in the framework of partial equivalence relations. Semantics exist for related theories (standard Nuprl or Martin-Löf type theory [67, 68]) in the frameworks of set theory [54] and domain theory [87, 81, 80], among others [4, 10], and could probably be developed for my theory as well.

4.4.1 Type Semantics

The semantics of types is characterized by the relations t_{1} = t_{2} ∈ T and T_{1} = T_{2} from Section 4.1.2. The first relation associates with each type T a partial equivalence relation,^{4} − = − ∈ T, over the set of terms. That relation dictates which terms belong to the type and which are equal within that type. The second relation is another partial equivalence relation over terms that dictates which terms are types and which are equal types.

4A symmetric and transitive relation.

t ∈ T iff t = t ∈ T T type iff T = T T_{1} = T_{2} iff ∃T′_{1}, T′_{2}. (T_{1} ⇓ T′_{1}) ∧ (T_{2} ⇓ T′_{2}) ∧ (T′_{1} = T′_{2}) t_{1} = t_{2} ∈ T iff ∃t′_{1}, t′_{2}, T′. (t_{1} ⇓ t′_{1}) ∧ (t_{2} ⇓ t′_{2}) ∧ (T ⇓ T′) ∧ (t′_{1} = t′_{2} ∈ T′)

¬(t_{1} = t_{2} ∈ Void) a = a′ ∈ Atom (a, a′ atoms) iff a ≡ a′ n = n′ ∈ ℤ (n, n′ integers) iff n ≡ n′ t = t′ ∈ 𝔼 iff t↓ ∧ t′↓ inj_{1}(a) = inj_{1}(a′) ∈ A + B iff A + B type ∧ a = a′ ∈ A inj_{2}(b) = inj_{2}(b′) ∈ A + B iff A + B type ∧ b = b′ ∈ B ⟨a, b⟩ = ⟨a′, b′⟩ ∈ Σx:A.B iff Σx:A.B type ∧ (a = a′ ∈ A) ∧ (b = b′ ∈ B[a/x]) λx.b = λx.b′ ∈ Πx:A.B iff Πx:A.B type ∧ ∀a = a′ ∈ A. b[a/x] = b′[a′/x] ∈ B[a/x] λx.b = λx.b′ ∈ {f | x:A → B} iff {f | x:A → B} type ∧ ∀a = a′ ∈ A. b[a/x] = b′[a′/x] ∈ B[λx.b, a/f, x] t = t′ ∈ \overline{T} iff \overline{T} type ∧ (t↓ ⇔ t′↓) ∧ (t↓ ⇒ t = t′ ∈ T) a = a′ ∈ {x : A | B} iff {x : A | B} type ∧ a = a′ ∈ A ∧ ∃b. b ∈ B[a/x] a = a′ ∈ xy:A//B iff (xy:A//B) type ∧ a ∈ A ∧ a′ ∈ A ∧ ∃b. b ∈ B[a, a′/x, y] ⋆ ∈ (a = a′ in A) iff (a = a′ in A) type ∧ (a = a′ ∈ A) ⋆ ∈ (A ⪯ B) iff (A ⪯ B) type ∧ ∀a = a′ ∈ A. a = a′ ∈ B ⋆ ∈ (t ≤ t′) iff (t ≤ t′) type ∧ ∃n, n′. t ⇓ n ∧ t′ ⇓ n′ ∧ n ≤ n′ ⋆ ∈ (a in! A) iff (a in! A) type ∧ a↓ ⋆ ∈ (A total) iff (A total) type ∧ ∀t ∈ A. t↓ ⋆ ∈ (A admiss) iff (A admiss) type ∧ Adm(A)

For T ≃ T′ iff T = T′, and for T ≃ T′ iff T = T′ ∈ 𝕌_{i}: Void ≃ Void Atom ≃ Atom ℤ ≃ ℤ 𝔼 ≃ 𝔼 A + B ≃ A′ + B′ iff A ≃ A′ ∧ B ≃ B′ Σx:A.B ≃ Σx:A′.B′ iff A ≃ A′ ∧ ∀a = a′ ∈ A. B[a/x] ≃ B′[a′/x] Πx:A.B ≃ Πx:A′.B′ iff A ≃ A′ ∧ ∀a = a′ ∈ A. B[a/x] ≃ B′[a′/x] {f | x:A → B} ≃ {f | x:A′ → B′} iff A ≃ A′ ∧

∃P, < . ∀a_{1} = a′_{1} ∈ A. ∀a_{2} = a′_{2} ∈ A. P [a_{1}, a_{2}/x, y] ≃ P [a′_{1}, a′_{2}/x, y] ∧ ∀a, a′. a < a′ ⇔ (∃e. e ∈ P [a, a′/x, y]) ∧ < is well-founded ∧ ∀a = a′ ∈ A. ∀t = t′ ∈ {f | x:{e : A | P [e, a/x, y]} → B}.

B[t, a/f, x] ≃ B′[t′, a′/f, x]

\overline{T} ≃ \overline{T′} iff T ≃ T′ ∧ ∀t ∈ T. t↓ {x : A | B} ≃ {x : A′ | B′} iff A ≃ A′ ∧ ∀a = a′ ∈ A. B[a/x] ≃ B[a′/x] ∧

∀a = a′ ∈ A. B′[a/x] ≃ B′[a′/x] ∧

∃t. t ∈ Πx:A. B → B′ ∧ ∃t. t ∈ Πx:A. B′ → B

(xy:A//B) ≃ (xy:A′//B′) iff A ≃ A′ ∧ ∀a_{1} = a′_{1} ∈ A. ∀a_{2} = a′_{2} ∈ A. B[a_{1}, a_{2}/x, y] ≃ B[a′_{1}, a′_{2}/x, y] ∧

∀a_{1} = a′_{1} ∈ A. ∀a_{2} = a′_{2} ∈ A. B′[a_{1}, a_{2}/x, y] ≃ B′[a′_{1}, a′_{2}/x, y] ∧
∃t. t ∈ Πx:A. Πy:A. B → B′ ∧ ∃t. t ∈ Πx:A. Πy:A. B′ → B ∧
∃t. t ∈ Πx:A. B[x/y] ∧ ∃t. t ∈ Πx:A. Πy:A. B → B[y, x/x, y] ∧
∃t. t ∈ Πx:A. Πy:A. Πz:A. B → B[y, z/x, y] → B[z/y]

(a_{1} = a_{2} in A) ≃

(a′_{1} = a′_{2} in A′) iff A ≃ A′ ∧ a_{1} = a′_{1} ∈ A ∧ a_{2} = a′_{2} ∈ A (A ⪯ B) ≃ (A′ ⪯ B′) iff A ≃ A′ ∧ B ≃ B′ (t_{1} ⪯ t_{2}) ≃ (t′_{1} ⪯ t′_{2}) iff t_{1} = t′_{1} ∈ ℤ ∧ t_{2} = t′_{2} ∈ ℤ (a in! A) ≃ (a′ in! A′) iff A ≃ A′ ∧ a = a′ ∈ \overline{A} (A total) ≃ (A′ total) iff A ≃ A′ (A admiss) ≃ (A′ admiss) iff A ≃ A′ 𝕌_{i} type 𝕌_{i} ∈ 𝕌_{j} iff i < j

Figure 4.5: Type Specifications

These relations are specified in Figure 4.5. Unfortunately, the specification contains negative occurrences of the relations being defined, so it cannot be taken as a valid inductive definition. However, examination reveals that each clause of the specification uses the relations only at types smaller than the type at issue in the clause. This suggests a way that we may rephrase the specification as a valid inductive definition. The technique I use is due to Allen [5]; my contribution here is its extension to a number of new types. An alternative but similar technique is that of Harper [40], who shows how the specification can be rephrased to define an explicit set theoretic relation as the least fixed point of a monotone operator.

The principal notion in the construction is that of a type system. A candidate type system τ is a relation τT T′ϕ between closed terms T and T′ and binary relations ϕ over closed terms. The intended reading of τT T′ϕ is that T and T′ are equal types and ϕ is their equality relation.

Definition 4.8 A type system is a candidate type system τ that satisfies seven properties:

• τ is uniquely valued: if τT T′ϕ and τT T′ϕ′ then ϕ is ϕ′

• τ is type symmetric: if τT T′ϕ then τT′T ϕ

• τ is type transitive: if τT_{1}T_{2}ϕ and τT_{2}T_{3}ϕ then τT_{1}T_{3}ϕ

• τ is type value respecting: if τT T ϕ and T ↦ T′ then τT T′ϕ

• τ is term symmetric: if τT T′ϕ then ϕ is symmetric

• τ is term transitive: if τT T′ϕ then ϕ is transitive

• τ is term value respecting: if τT T′ϕ and t ϕ t and t ↦ t′ then t ϕ t′

The goal is to build a type system that captures the intended meanings of the types. I do this be defining a series of operators on candidate type systems. Note that each operator uses it argument τ only in positive positions, so they may be assembled into a valid inductive definition.

The first (and most important) step of the construction is to define, for each type constructor, an operator on candidate type systems. Given candidate type system τ, each operator is to return a type system that contains all types built using its type constructor from types in τ. For example, Prod(τ) will contain all product types built from types in τ. These operators define the meanings of the type constructors. Their definitions appear in Figures 4.6 through 4.8.

Next we define another operator Close on candidate type systems. Given type system τ, Close(τ) contains all types built from types in τ using any type constructor other than universes. The intention is that the argument τ define all universes up to some 𝕌_{i}, in which case Close(τ) will define all types built from those universes, that is, all members of 𝕌_{i+1}. Close(τ) is defined to be the smallest candidate type system such that:

Close(τ)T T′ϕ iff τT T′ϕ ∨ (⋁_{op∈Types} op(Close(τ))T T′ϕ)
where Types = \{ \begin{matrix}Void, Atom, Int, Every, Union, \\ Prod, Fun, VFun, Bar, Eq, Sub, \\ Ineq, Conv, Total, Adm, Set, Quot \end{matrix} \}

Next we use mutual induction to define two series of candidate type systems, Univ_{i} and Nuprl_{i}. Univ_{i} defines all universes 𝕌_{j} where j < i, and Nuprl_{i} defines all types built up from those

Void(−)T T′ϕ iff T ⇓ Void ∧ T′ ⇓ Void ∧ ∀t, t′. ¬(t ϕ t′)
Atom(−)T T′ϕ iff T ⇓ Atom ∧ T′ ⇓ Atom ∧ ∀t, t′. t ϕ t′ ⇔ (∃a ∈ {atoms}. t ⇓ a ∧ t′ ⇓ a)
Int(−)T T′ϕ iff T ⇓ ℤ ∧ T′ ⇓ ℤ ∧ ∀t, t′. t ϕ t′ ⇔ (∃n ∈ {integers}. t ⇓ n ∧ t′ ⇓ n)
Every(−)T T′ϕ iff T ⇓ 𝔼 ∧ T′ ⇓ 𝔼 ∧ ∀t, t′. t ϕ t′ ⇔ (t↓ ∧ t′↓)
Union(τ)T T′ϕ iff ∃A, B, A′, B′, α, β.

T ⇓ A + B ∧ T′ ⇓ A′ + B′ ∧ τAA′α ∧ τBB′β ∧ ∀t, t′. t ϕ t′ ⇔

(∃a, a′. t ⇓ inj_{1}(a) ∧ t′ ⇓ inj_{1}(a′) ∧ a α a′) ∨

(∃b, b′. t ⇓ inj_{2}(b) ∧ t′ ⇓ inj_{2}(b′) ∧ b α b′)
Prod(τ)T T′ϕ iff ∃A, B, A′, B′, α, β_{(−)}.

T ⇓ Σx:A.B ∧ T′ ⇓ Σx:A′.B′ ∧ τAA′α ∧ ∀a, a′. a α a′ ⇒ τ(B[a/x])(B′[a′/x])β_{a} ∧ ∀t, t′. t ϕ t′ ⇔

∃a, b, a′, b′. t ⇓ ⟨a, b⟩ ∧ t′ ⇓ ⟨a′, b′⟩ ∧ a α a′ ∧ b β_{a} b′

Fun(τ)T T′ϕ iff ∃A, B, A′, B′, α, β_{(−)}.

T ⇓ Πx:A.B ∧ T′ ⇓ Πx:A′.B′ ∧ τAA′α ∧ ∀a, a′. a α a′ ⇒ τ(B[a/x])(B′[a′/x])β_{a} ∧ ∀t, t′. t ϕ t′ ⇔

∃b, b′. \begin{matrix}t ⇓ λx.b ∧ t′ ⇓ λx.b′ ∧ \\ ∀a, a′. a α a′ ⇒ b[a/x] β_{a} b′[a′/x] \end{matrix}

VFun(τ)T T′ϕ iff ∃A, B, A′, B′, P, <, α, γ_{(−)}, δ_{(−,−)}, ρ_{(−,−)}.

T ⇓ {f | x:A → B} ∧ T ⇓ {f | x:A′ → B′} ∧ τAA′α ∧ ∀a_{1}, a_{2}, a′_{1}, a′_{2}. a_{1} α a′_{1} ⇒ a_{2} α a′_{2} ⇒

τ(P [a_{1}, a_{2}/x, y])(P [a′_{1}, a′_{2}/x, y])ρ_{(a_{1},a_{2})} ∧ ∀a, a′. a < a′ ⇔ (∃e. e ρ_{(a,a′)} e) ∧ < is well-founded ∧ ∀a, t, a′, t′. a α a′ ⇒ t γ_{a} t′ ⇒ τ(B[t, a/f, x])(B′[t′, a′/f, x])δ_{(t,a)} ∧ ∀e, t, t′. e α e ⇒

t γ_{e} t′ ⇔

∃b, b′. \begin{matrix}t ⇓ λx.b ∧ t′ ⇓ λx.b′ ∧ \\ ∀a, a′. a α a′ ⇒ a < e ⇒ b[a/x] δ_{(t,a)} b′[a′/x]) ∧ \end{matrix}

∀t, t′. t ϕ t′ ⇔

∃b, b′. \begin{matrix}t ⇓ λx.b ∧ t′ ⇓ λx.b′ ∧ \\ ∀a, a′. a α a′ ⇒ b[a/x] δ_{(t,a)} b′[a′/x] \end{matrix}

Bar(τ)T T′ϕ iff ∃A, A′, α.

T ⇓ \overline{A} ∧ T′ ⇓ \overline{A′} ∧ τAA′α ∧ ∀a. a α a ⇒ a↓ ∧ ∀t, t′. t ϕ t′ ⇔

(t↓ ⇔ t′↓) ∧ (t↓ ⇒ t α t′)

Figure 4.6: Type Definitions (Data Types)

Eq(τ)T T′ϕ iff ∃A, A′, a_{1}, a_{2}, a′_{1}, a′_{2}, α.

T ⇓ (a_{1} = a_{2} in A) ∧ T′ ⇓ (a′_{1} = a′_{2} in A′) ∧

τAA′α ∧ a_{1} α a′_{1} ∧ a_{2} α a′_{2} ∧

∀t, t′. t ϕ t′ ⇔ (t ⇓ ⋆ ∧ t′ ⇓ ⋆ ∧ a_{1} α a_{2})

Sub(τ)T T′ϕ iff ∃A, B, A′, B′, α, β.

T ⇓ (A ⪯ B) ∧ T′ ⇓ (A′ ⪯ B′) ∧ τAA′α ∧ τBB′β ∧ ∀t, t′. t ϕ t′ ⇔ (t ⇓ ⋆ ∧ t′ ⇓ ⋆ ∧ ∀a, a′. a α a′ ⇒ a β a′)

Ineq(−)T T′ϕ iff ∃e_{1}, e_{2}, e′_{1}, e′_{2}. ∃n_{1}, n_{2} ∈ {integers}.
T ⇓ (e_{1} ≤ e_{2}) ∧ T′ ⇓ (e′_{1} ≤ e′_{2}) ∧

e_{1} ⇓ n_{1} ∧ e′_{1} ⇓ n_{1} ∧ e_{2} ⇓ n_{2} ∧ e′_{2} ⇓ n′_{2} ∧ ∀t, t′. t ϕ t′ ⇔ (t ⇓ ⋆ ∧ t′ ⇓ ⋆ ∧ n_{1} ≤ n_{2})

Conv(τ)T T′ϕ iff ∃A, A′, a, a′, α.

T ⇓ (a in! A) ∧ T′ ⇓ (a′ in! A′) ∧ τAA′α ∧ (∀e. e α e ⇒ e↓) ∧ (a↓ ⇔ a′↓) ∧ (a↓ ⇒ a α a′) ∧

∀t, t′. t ϕ t′ ⇔ (t ⇓ ⋆ ∧ t′ ⇓ ⋆ ∧ a↓)
Total(τ)T T′ϕ iff ∃A, A′, α.

T ⇓ (A total) ∧ T′ ⇓ (A′ total) ∧ τAA′α ∧ ∀t, t′. t ϕ t′ ⇔ (t ⇓ ⋆ ∧ t′ ⇓ ⋆ ∧ ∀a. a α a ⇒ a↓)

Adm(τ)T T′ϕ iff ∃A, A′, α.

T ⇓ (A admiss) ∧ T′ ⇓ (A′ admiss) ∧ τAA′α ∧

∀t, t′. t ϕ t′ ⇔
t ⇓ ⋆ ∧ t′ ⇓ ⋆ ∧

∀f, e, e′. (∃j. ∀k ≥ j. e^{[k]_{f}} α e^{′[k]_{f}}) ⇒ e^{[ω]_{f}} α e^{′[ω]_{f}}

Figure 4.7: Type Definitions (Assertions)

universes.

Univ_{i}T T′ϕ iff ∃j < i. T ⇓ 𝕌_{j} ∧ T′ ⇓ 𝕌_{j} ∧
∀A, A′. A ϕ A′ ⇔ ∃α. Nuprl_{j}AA′α
Nuprl_{i} =^{def} Close(Univ_{i})

Finally we may define a candidate type system Univ_{ω} that defines all universes, and define the desired Nuprl_{ω} to be its closure:

Univ_{ω}T T′ϕ iff ∃i. T ⇓ 𝕌_{i} ∧ T′ ⇓ 𝕌_{i} ∧
∀A, A′. A ϕ A′ ⇔ ∃α. Nuprl_{i}AA′α
Nuprl_{ω} =^{def} Close(Univ_{ω})

We may now define the desired equality relations using Nuprl_{ω}:

Definition 4.9

• T_{1} = T_{2} iff ∃ϕ. Nuprl_{ω}T_{1}T_{2}ϕ

• t_{1} = t_{2} ∈ T iff ∃ϕ. Nuprl_{ω}T T ϕ ∧ t_{1} ϕ t_{2}

It remains to show that the equality relations have the desired properties. First we must show that Nuprl_{ω} is actually a type system, as intended. Then we will observe that Nuprl_{ω} adheres to the specification in Figure 4.5.

Lemma 4.10 If τ is a type system, then for each op ∈ Types, op(τ) is a type system.

Lemma 4.11 Each operator in Types defines disjoint types from the others and from τ (when τ defines only universes). Formally, if

Set(τ)T T′ϕ iff ∃A, B, A′, B′, α, β_{(−)}, β′_{(−)}, e, e′.

T ⇓ {x : A | B} ∧ T ⇓ {x : A′ | B′} ∧ τAA′α ∧ ∀a, a′. a α a′ ⇒ τ(B[a/x])(B[a′/x])β_{a} ∧ ∀a, a′. a α a′ ⇒ τ(B′[a/x])(B′[a′/x])β′_{a} ∧ (B implies B′) ∀a, b, a′, b′. a α a′ ⇒ b β_{a} b′ ⇒ e[a, b/x, y] β′_{a} e[a′, b′/x, y] ∧ (B′ implies B) ∀a, b, a′, b′. a α a′ ⇒ b β′_{a} b′ ⇒ e′[a, b/x, y] β_{a} e′[a′, b′/x, y] ∧ ∀t, t′. t ϕ t′ ⇔ (t α t′ ∧ ∃b. b β_{t} b)

Quot(τ)T T′ϕ iff ∃A, B, A′, B′, α, β_{(−,−)}, β′_{(−,−)}, e, e′, e_{r}, e_{s}, e_{t}.

T ⇓ xy:A//B ∧ T ⇓ xy:A′//B′ ∧ τAA′α ∧ ∀a_{1}, a_{2}, a′_{1}, a′_{2}. a_{1} α a′_{1} ⇒ a_{2} α a′_{2} ⇒

τ(B[a_{1}, a_{2}/x, y])(B[a′_{1}, a′_{2}/x, y])β_{(a_{1},a_{2})} ∧ ∀a_{1}, a_{2}, a′_{1}, a′_{2}. a_{1} α a′_{1} ⇒ a_{2} α a′_{2} ⇒

τ(B′[a_{1}, a_{2}/x, y])(B′[a′_{1}, a′_{2}/x, y])β′_{(a_{1},a_{2})} ∧ (B implies B′) ∀a_{1}, a_{2}, a′_{1}, a′_{2}, b, b′. a_{1} α a′_{1} ⇒ a_{2} α a′_{2} ⇒ b β_{(a_{1},a_{2})} b′ ⇒

e[a_{1}, a_{2}, b/x, y, z] β′_{(a_{1},a_{2})} e[a′_{1}, a′_{2}, b′/x, y, z] ∧ (B′ implies B) ∀a_{1}, a_{2}, a′_{1}, a′_{2}, b, b′. a_{1} α a′_{1} ⇒ a_{2} α a′_{2} ⇒ b β′_{(a_{1},a_{2})} b′ ⇒

e[a_{1}, a_{2}, b/x, y, z] β_{(a_{1},a_{2})} e[a′_{1}, a′_{2}, b′/x, y, z] ∧ (reflexivity) ∀a, a′. a α a′ ⇒ e_{r}[a/x] β_{(a,a)} e_{r}[a′/x] ∧ (symmetry) ∀a_{1}, a_{2}, a′_{1}, a′_{2}, b, b′. a_{1} α a′_{1} ⇒ a_{2} α a′_{2} ⇒ b β_{(a_{1},a_{2})} b′ ⇒

e_{s}[a_{1}, a_{2}, b/x, y, z] β_{(a_{2},a_{1})} e_{s}[a′_{1}, a′_{2}, b′/x, y, z] ∧ (transitivity) ∀a_{1}, a_{2}, a_{3}, a′_{1}, a′_{2}, a′_{3}, b_{1}, b_{2}, b′_{1}, b′_{2}.

a_{1} α a′_{1} ⇒ a_{2} α a′_{2} ⇒ a_{3} α a′_{3} ⇒

b_{1} β_{(a_{1},a_{2})} b′_{1} ⇒ b_{2} β_{(a_{2},a_{3})} b′_{2} ⇒
e_{t}[a_{1}, a_{2}, a_{3}, b_{1}, b_{2}/x, y, z, v, w] β_{(a_{1},a_{3})}
e_{s}[a′_{1}, a′_{2}, a′_{3}, b′_{1}, b′_{2}/x, y, z, v, w] ∧

∀t, t′. t ϕ t′ ⇔ (t α t ∧ t′ α t′ ∧ ∃b. b β_{(t,t′)} b)

Figure 4.8: Type Definitions (Set and Quotient)

• τ is type symmetric and type transitive, and

• τ defines only universes (that is, if τT T ϕ then T ⇓ 𝕌_{i} for some i), and

• Close(τ)T_{1}T′_{1}ϕ_{1} and Close(τ)T_{2}T′_{2}ϕ_{2}, and

• either T_{1} ≡ T_{2} or T_{1} ≡ T′_{2},

then either τT_{1}T′_{1}ϕ_{1} and τT_{2}T′_{2}ϕ_{2}, or for some op ∈ Types, op(Close(τ))T_{1}T′_{1}ϕ_{1} and op(Close(τ))T_{2}T′_{2}ϕ_{2}.

Lemma 4.12 If τ is a type system and if τ defines only universes, then Close(τ) is a type system.

Lemma 4.13 For all i, Univ_{i} and Nuprl_{i} are type systems.

Lemma 4.14 Univ_{ω} and Nuprl_{ω} are type systems.

Proof

First show that Univ_{ω} is a type system: All but unique valuation and type transitivity are directly by Lemma 4.13. For unique valuation and type transitivity, use Lemma 4.13 with the observation that Univ_{i} is cumulative: for all i < j, if Univ_{i}T T′ϕ then Univ_{j}T T′ϕ. Then Nuprl_{ω} is a type system by Lemma 4.12. □

Proposition 4.15 The equality relations T_{1} = T_{2} and t_{1} = t_{2} ∈ T adhere to the specification in Figure 4.5.

4.4.2 Judgement Semantics

I begin the semantics for judgements by defining the notion of an assignment of closed terms to variables:

Definition 4.16

• An assignment is a sequence of pairs ⟨x_{1}, t_{1}⟩ ⋯ ⟨x_{n}, t_{n}⟩ such that each x_{i} is a distinct variable and each t_{i} is a closed term.

• The application α(t) of an assignment α to a term t, where α ≡ ⟨x_{1}, t_{1}⟩ ⋯ ⟨x_{n}, t_{n}⟩, is t[t_{1} ⋯ t_{n}/x_{1} ⋯ x_{n}].

As discussed in Section 4.2.1, the intended meaning of the sequent H ⊢_{ν} C ◁ t is that α(C) = α′(C) and α(t) = α′(t) ∈ α(C) when α and α′ are equal assignments for the hypotheses H. In order to formalize this as a semantics for judgements, I must define exactly what is meant by equal assignments.

The obvious idea is to say that assignments are equal when their corresponding terms are equal in the appropriate types. We may call this notion “assignment similarity”:

Definition 4.17 Assignment similarity (written α_{1} ≈ α_{2} ∈ H) is the smallest relation such that:

• ϵ ≈ ϵ ∈ ϵ

• α_{1}⟨x, t_{1}⟩ ≈ α_{2}⟨x, t_{2}⟩ ∈ (H; x : T) if α_{1} ≈ α_{2} ∈ H and t_{1} = t_{2} ∈ α_{1}(T)

This notion provides a good starting point, but it will not serve as the needed notion of assignment equality, because it is neither symmetric nor transitive. Symmetry does not hold because it is not generally the case for similar assignments α_{1} and α_{2} that α_{1}(T) = α_{2}(T), so t_{1} = t_{2} ∈ α_{1}(T) does not imply t_{2} = t_{1} ∈ α_{2}(T). Transitivity fails for the same reason.

The solution is to impose on the inductive case the additional requirement that α_{1}(T) = α_{2}(T). The leads to the first of three definitions of assignment equality:

Definition 4.18 Assignment equality with minimal functionality (written α_{1} = α_{2} ∈_{min} H) is the smallest relation such that:

• ϵ = ϵ ∈_{min} ϵ

• α_{1}⟨x, t_{1}⟩ = α_{2}⟨x, t_{2}⟩ ∈_{min} (H; x : T) if

– α_{1} = α_{2} ∈_{min} H, and
– t_{1} = t_{2} ∈ α_{1}(T), and
– α_{1}(T) = α_{2}(T)

Proposition 4.19 Assignment equality with minimal functionality is symmetric and transitive.

This notion of equality is called minimal functionality because it uses the minimum extra constraint (over similarity) to ensure symmetry and transitivity. By strengthening that constraint, we can get two other interesting forms of assignment equality. Note that in the third clause of the inductive case of Definition 4.18, the assignments α_{1} and α_{2} are similar (since clearly α_{1} = α_{2} ∈_{min} H implies α_{1} ≈ α_{2} ∈ H) and fixed. We define pointwise functionality by allowing one of the similar assignments to vary, and we define full functionality by allowing both of the similar assignments to vary:

Definition 4.20 Assignment equality with pointwise functionality (written α_{1} = α_{2} ∈_{pw} H) is the smallest relation such that:

• ϵ = ϵ ∈_{pw} ϵ

• α_{1}⟨x, t_{1}⟩ = α_{2}⟨x, t_{2}⟩ ∈_{pw} (H; x : T) if

– α_{1} = α_{2} ∈_{pw} H, and

– t_{1} = t_{2} ∈ α_{1}(T), and

– for all α, if α_{1} ≈ α ∈ H then α_{1}(T) = α(T)

Definition 4.21 Assignment equality with full functionality (written α_{1} = α_{2} ∈_{full} H) is the smallest relation such that:

• ϵ = ϵ ∈_{full} ϵ

• α_{1}⟨x, t_{1}⟩ = α_{2}⟨x, t_{2}⟩ ∈_{full} (H; x : T) if

– α_{1} = α_{2} ∈_{full} H, and

– t_{1} = t_{2} ∈ α_{1}(T), and

– for all α, α′, if α ≈ α′ ∈ H then α(T) = α′(T)

Following are some important properties of pointwise and full functionality. For any of the three forms of functionality, I write α ∈ H to mean α = α ∈ H.

Proposition 4.22

• If α ∈_{pw} H, then for any α′, α ≈ α′ ∈ H implies α = α′ ∈_{pw} H

• If α ∈_{full} H, then for any α_{1}, α_{2}, α_{1} ≈ α_{2} ∈ H implies α_{1} = α_{2} ∈_{full} H

• Assignment equality with pointwise functionality is symmetric and transitive.

• Assignment equality with full functionality is symmetric and transitive.

We can now define the semantics of Nuprl judgements. Following Constable et al. [19], I use pointwise functionality. (An avenue for future research is to explore semantics based on full [6] or minimal functionality.) The semantics given by Constable et al. [19] is equivalent to this one, but is considerably more complicated.

Definition 4.23 The judgement H ⊢_{ν} C ◁ t is valid if for all assignments α and α′, α = α′ ∈_{pw} H implies α(C) = α′(C) and α(t) = α′(t) ∈ α(C). The judgement ⊢_{ν} t ∼ t′ is valid if t ∼ t′.

Theorem 4.24 (Nuprl Soundness) Any judgement derivable in the Nuprl proof rules is valid.

Proof

By induction on derivations.

Corollary 4.25 (Nuprl Consistency) The Nuprl proof rules are consistent.

Proof

The judgement ⊢_{ν} Void ◁ ⋆ is invalid, and therefore unprovable. □

4.4.3 Classical Reasoning

As discussed in Section 4.1.5, classical reasoning principles are semantically invalid within the type theory. In the Nuprl semantics, validity of the excluded middle principle (∀P. P ∨ ¬P) would imply the existence of a computable function that would determine the truth or falsity of every proposition. However, in the underlying metatheory, we may consider using classical reasoning principles.

The proof rules given in Appendix B and not marked as classical may all be proven valid without classical reasoning. However, there are some additional rules that may be shown valid in a classical metatheory.

The primary such rule is a weak form of the excluded middle principle:

H ⊢ ∀P:ℙ_{i}. ↓(P ∨ ¬P) ◁ λx.⋆ (XM)

Proposition 4.26 The rule XM is semantically valid in a classical interpretation.

Note that this works because of the squashing of P ∨¬P. The alternative axiom ↓(∀P:ℙ_{i}. P ∨¬P) is not semantically valid, because it still implies the existence of a general decision function, even though that function is still not provided. In Chapter 5 we will see another example of important rules validated by a classical metatheory but not a constructive metatheory. These are the coadmissibility rules for partial types (Rules PWC and PC).

One practical use of the XM rule is for fixpoint induction (Section 4.3.3). Recall that the fixpoint induction rule generates the subgoal H; x : \overline{T}; y : (x in! T ⇒ P) ⊢_{ν} P. This subgoal may be proven from the simpler subgoal H ⊢_{ν} P [⊥/x] using the XM rule (and some of the other subgoals of the fixpoint induction rule): Instantiating XM gives ↓(x in! T ∨ ¬(x in! T)). Then ↓P can be proven by cases, invoking hypothesis y if x in! T and substituting ⊥ for x if ¬(x in! T). By subgoal 8, we may conclude P from ↓P.

Admissibility

One of the earliest logical theorem provers was the LCF system [38], based on the logic of partial computable functions [88, 89]. Although LCF enjoyed many groundbreaking successes, one problem it faced was that, although it supported a natural notion of partial function, it had difficulty expressing the notion of a total function. Later theorem provers based on constructive type theory, such as Nuprl [19], based on Martin-Löf type theory [68], and Coq [8], based on the Calculus of Constructions [29], faced the opposite problem; they had a natural notion of total functions, but had difficulty dealing with partial functions. The lack of partial functions seriously limited the scope of those theorem provers, because it made them unable to reason about programs in real programming languages where recursion does not always necessarily terminate.

This problem may be addressed in type theory by the partial type discussed in Chapter 4. In a partial type theory, recursively defined objects may be typed using the fixpoint principle: if f has type \overline{T} → \overline{T} then fix(f) has type \overline{T}. However, the fixpoint principle is not valid for every type T; it is only valid for types that are admissible. This phenomenon was not unknown to LCF; LCF used the related device of fixpoint induction, which was valid only for admissible predicates. When the user attempted to invoke fixpoint induction, the system would automatically check that the goal was admissible using a set of syntactic rules [55].

Despite their obvious uses in program analysis, partial types have seen little use in theorem proving systems [25, 9, 7]. This is due in large part to two problems: partial type extensions have been developed only for fragments of type theory that do not include equality, and too few types have been known to be admissible. I addressed the former problem in Chapter 4; in this chapter I address the latter.

Smith [92] gave a significant class of admissible types for a Nuprl-like theory, but his class required product types to be non-dependent. The type Σx:A.B (where x appears free in B) was explicitly excluded. Later, Smith [91] extended his class to include some dependent products Σx:A.B, but disallowed any free occurrences of x to the left of an arrow in B. Partial type extensions to Coq [7] were also restrictive, assuming function spaces to be the only type constructor. These restrictions are quite strong; dependent products are used in encodings of modules [65], objects [83], algebras [57], and even such simple devices as variant records. Furthermore, ruling out dependent products disallows reasoning using fixpoint induction as in LCF. (This is explained further in Section 5.1.) Finally, the restriction is particularly unsatisfying since most types used in practice do turn out to be admissible, and may be shown so by metatheoretical reasoning.

In this chapter I present a very wide class of admissible types using two devices, a condition called predicate-admissibility and a monotonicity condition. In particular, many dependent products may be shown to be admissible. Predicate-admissibility relates to when the limit of a chain of type approximations contains certain terms, whereas admissibility relates to the membership of a single type. Monotonicity is a simpler condition that will be useful for showing types admissible that do not involve partiality.

5.1 The Fixpoint Principle

The central issue of this chapter is the fixpoint principle:

f ∈ \overline{T} → \overline{T} ⇒ fix(f) ∈ \overline{T}

The fixpoint principle allows us to type recursively defined objects, such as recursive functions. Unfortunately, unlike in programming languages, where the principle can usually be invoked on arbitrary types, expressive type theories such as the one in this thesis contain types for which the fixpoint principle is not valid. I shall informally say that a type is admissible if the fixpoint principle is valid for that type and give a formal definition in Section 5.3. To make maximum use of a partial type theory, one wants as large a class of admissible types as possible.

In Section 5.3 I will explore two wide classes of admissible types, one derived from a predicateadmissibility condition and another derived from a monotonicity condition. But first, it is worthwhile to note that there are indeed inadmissible types:

Theorem 5.1 There exist inadmissible types.

Proof Sketch

This example is due to Smith [92]. Recall that ℕ = {n : ℤ | 0 ≤ n}. Let T be the type of functions that do not halt for all inputs, and let f be the function that halts on zero, and on any other n immediately recurses with n − 1. This is formalized as follows:

T =^{def} Σh:(ℕ → \overline{ℕ}). ((Πx:ℕ. h x in! ℕ) → Void)
f =^{def} λp.⟨λx. if x ≤_{Z} 0 then 0 else π_{1}(p)(x − 1), λy. ⋆⟩

Intuitively, any finite approximation of fix(f) will recurse some limited number of times and then give up, placing it in T, but fix(f) will halt for every input, excluding it from T. Formally, the function f has type \overline{T} → \overline{T}, but fix(f) ∉ \overline{T}. (The proof of these two facts appears in Appendix C.) Therefore T is not admissible.

5.2 Computational Lemmas

Before presenting my main results in Section 5.3, I first require some lemmas about the computational behavior of the fixpoint operator. The central result is that fix(f) is the least upper bound of the finite approximations ⊥, f(⊥), f(f(⊥)), … with regard to a computational approximation relation defined below. The compactness of fix (if fix(f) halts then one of its finite approximations halts) will be a simple corollary of this result. However, the proof of the least upper bound theorem is considerably more elegant than most proofs of compactness.

5.2.1 Computational Approximation

For convenience, throughout this section we will frequently consider terms using a unified representation scheme for terms: A term is either a variable or a compound term θ(x_{11} ⋯ x_{1k_{1}}.t_{1}, …, x_{n1} ⋯ x_{nk_{n}}.t_{n}) where the variables x_{i1}, …, x_{ik_{i}} are bound in the subterm t_{i}. For example, the term Πx:T_{1}.T_{2} is represented Π(T_{1}, x.T_{2}) and the term ⟨t_{1}, t_{2}⟩ is represented ⟨⟩(t_{1}, t_{2}).

Informally speaking, a term t_{1} approximates the term t_{2} when: if t_{1} converges to a canonical form then t_{2} converges to a canonical form with the same outermost operator, and the subterms of t_{1}’s canonical form approximate the corresponding subterms of t_{2}’s canonical form. The formal definition appears below and is due to Howe [53].^{1} Following Howe, when R is a binary relation on closed terms, I adopt the convention extending R to possibly open terms that if t and t′ are possibly open then t R t′ if and only if σ(t) R σ(t′) for every substitution σ such that σ(t) and σ(t′) are closed.

Definition 5.2 (Computational Approximation)

• Let R be a binary relation on closed terms and suppose e and e′ are closed. Then e C(R) e′ exactly when if e ⇓ θ(x⃗_{1}.t_{1}, …, x⃗_{n}.t_{n}) then there exists some closed e′′ = θ(x⃗_{1}.t′_{1}, …, x⃗_{n}.t′_{n}) such that e′ ⇓ e′′ and t_{i} R t′_{i}.

• e ≤_{0} e′ whenever e and e′ are closed.

• e ≤_{i+1} e′ if and only if e C(≤_{i}) e′

• e ≤ e′ if and only if e ≤_{i} e′ for every i

Definition 5.3 (Computational Equivalence) The terms e and e′ are computationally equivalent (e ∼ e′) if and only if e ≤ e′ and e′ ≤ e.

The following are facts about computational approximation that will be used without explicit reference. The first two follow immediately from the definition, the third is easy using determinism (Proposition 4.2) and the last is proven using Howe’s method [53].

Proposition 5.4

• ≤ and ≤_{i} are reflexive and transitive.

• If t ↦ t′ then t′ ≤ t and t′ ≤_{i} t.

• If t ↦ t′ then t ≤ t′ and t ≤_{i} t′.

Lemma 5.5 (Congruence) If e ≤ e′ and t ≤ t′ then e[t/x] ≤ e′[t′/x].

1Howe’s definition actually differs slightly from the one here; he defines ≤ as the greatest fixed point of the operator C. It is not difficult to show that the two definitions are equivalent, as long as the computation system is deterministic (Proposition 4.2). If the computation system is nondeterministic, the definition here fails to be a fixed point, and the more complicated greatest fixed point definition must be employed.

5.2.2 Finite Approximations

With this notion of computational approximation in hand, we may now show that the terms ⊥, f ⊥, f(f ⊥), … form a chain of approximations to the term fix(f). Let ⊥ be the divergent term fix(λx.x). Since ⊥ never converges, ⊥ ≤ t for any term t. Let f^{i} be defined as follows:

\begin{matrix}f^{0} & =^{def} & ⊥ \\ f^{i+1} & =^{def} & f(f^{i}) \end{matrix}

Certainly f^{0} ≤ f^{1}, since f^{0} ≡ ⊥. By congruence, f(f^{0}) ≤ f(f^{1}), and thus f^{1} ≤ f^{2}. Similarly, f^{i} ≤ f^{i+1} for all i. Thus f^{0}, f^{1}, f^{2}, … forms a chain; I now wish to show that fix(f) is an upper bound of the chain. Certainly f^{0} ≤ fix(f). Suppose f^{i} ≤ fix(f). By congruence f(f^{i}) ≤ f(fix(f)). Thus, since fix(f) ↦ f(fix(f)), it follows that f^{i+1} ≡ f(f^{i}) ≤ f(fix(f)) ≤ fix(f). By induction it follows that fix(f) is an upper bound of the chain. The following corollary follows from congruence and the definition of approximation:

Corollary 5.6 If there exists j such that e[f^{j}/x]↓ then e[fix(f)/x]↓. Moreover, the canonical forms of e[f^{j}/x] and e[fix(f)/x] must have the same outermost operator.

5.2.3 Least Upper Bound Theorem

In this section I summarize the proof of the least upper bound theorem. To begin, we need a lemma stating a general property of evaluation. Lemma 5.7 captures the intuition that closed, noncanonical terms that lie within a term being evaluated are not destructed; they either are moved around unchanged (the lemma’s first case) or are evaluated in place with the surrounding term left unchanged (the lemma’s second case). The variable x indicates positions where the term of interest is found and, in the second case, the variable y indicates which of those positions, if any, is about to be evaluated.

Lemma 5.7 If e_{1}[t/x] ↦ e_{2}, and e_{1}[t/x] is closed, and t is closed and noncanonical, then either

• there exists e′_{2} such that for any closed t′, e_{1}[t′/x] ↦ e′_{2}[t′/x], or

• there exist e′_{1} and t′ such that e_{1} ≡ e′_{1}[x/y], t ↦ t′ and for any closed t′′, e′_{1}[t′′, t/x, y] ↦ e′_{1}[t′′, t′/x, y].

It is worthwhile to note that Proposition 4.2 and Lemmas 5.5 and 5.7 are the only properties of evaluation used in the proof of the least upper bound theorem, and that these properties are true in computational systems with considerable generality. Consequently, the theorem may be used in a variety of applications beyond the computational system of this thesis.

Lemma 5.8 shows that fix terms may be effectively simulated in any particular computation by sufficiently large finite approximations. The lemma is simplified by using computational approximation instead of evaluation for the simulation, which makes it unnecessary to track which of the approximations are unfolded and which are not, an issue that often complicates compactness proofs.

Lemma 5.8 (Simulation) For all f, e_{1} and e_{2} (where f is closed and x is the only free variable of e_{1}), there exist j and e′_{2} such that if e_{1}[fix(f)/x] ↦^{∗} e_{2} then e_{2} ≡ e′_{2}[fix(f)/x] and for all k ≥ j, e′_{2}[f^{k−j}/x] ≤ e_{1}[f^{k}/x].

Theorem 5.9 (Least Upper Bound) For all f, t and e (where f is closed), if ∀j. e[f^{j}/x] ≤ t, then e[fix(f)/x] ≤ t.

Proof Sketch

By induction on l that e[fix(f)/x] ≤_{l} t. (The complete proof in Appendix C addresses free variables.) Suppose e[fix(f)/x] evaluates to some canonical form e′[fix(f)/x] (where e′ is chosen by Lemma 5.8). Let e′ be of the form θ(x⃗_{1}.t_{1}, …, x⃗_{n}.t_{n}). Using Lemma 5.8, the assumption ∀k. e[f^{k}/x] ≤ t, and transitivity, we may show that e′[f^{j}/x] ≤ t for all j. Therefore t ⇓ θ(x⃗_{1}.t′_{1}, …, x⃗_{n}.t′_{n}) and t_{i}[f^{j}/x] ≤ t′_{i} for all j. Now, by induction, t_{i}[fix(f)/x] ≤_{l} t′_{i}. Thus e[fix(f)/x] ≤_{l+1} t.

There are two easy corollaries to the least upper bound theorem. One is that fix(f) is the least fixed point of f, and the other is compactness.

Corollary 5.10 (Least Fixed Point) For all closed f and t, if f(t) ≤ t then fix(f) ≤ t.

Proof

Certainly f^{0} ≡ ⊥ ≤ t. Then f^{1} ≡ f(f^{0}) ≤ f(t) ≤ t. Similarly, by induction, f^{j} ≤ t for any j. Therefore fix(f) ≤ t by Theorem 5.9. □

Corollary 5.11 (Compactness) If f is closed and e[fix(f)/x]↓ then there exists some j such that e[f^{j}/x]↓. Moreover, the canonical forms of e[fix(f)/x] and e[f^{j}/x] must have the same outermost operator.

Proof

Suppose there does not exist j such that e[f^{j}/x]↓. Then e[f^{j}/x] ≤ ⊥ for all j. By Theorem 5.9, e[fix(f)/x] ≤ ⊥. Therefore e[fix(f)/x] does not converge, but this contradicts the assumption,^{2} so there must exist j such that e[f^{j}/x]↓. Since e[f^{j}/x] ≤ e[fix(f)/x], the canonical forms of e[f^{j}/x] and e[fix(f)/x] must have the same outermost operator. □

5.3 Admissibility

I am now ready to begin specifying some wide classes of types for which the fixpoint principle is valid. First we define admissibility. The simple property of validating the fixpoint principle is too specific to allow any good closure conditions to be shown easily, so we generalize a bit to define admissibility. A type is admissible if the upper bound t[fix(f)] of an approximation chain t[f^{0}], t[f^{1}], t[f^{2}], … belongs to the type whenever a cofinite subset of the chain belongs to the type. This is formalized as Definition 5.13, but first I define some convenient notation.

Notation 5.12 For any natural number j, the notation t^{[j]_{f}} means t[f^{j}/w], and the notation t^{[ω]_{f}} means t[fix(f)/w]. Also, the f subscript is dropped when the intended term f is unambiguously clear.

Definition 5.13 A type T is admissible (abbreviated Adm(T)) if:

∀f, t, t′. (∃j. ∀k ≥ j. t^{[k]} = t^{′[k]} ∈ T) ⇒ t^{[ω]} = t^{′[ω]} ∈ T

2Although this proof is non-constructive, a slightly less elegant constructive proof may be derived directly from Lemma 5.8.

As expected, admissibility is sufficient to guarantee applicability of the fixpoint principle:

Theorem 5.14 For any T and f, if T is admissible and f = f′ ∈ \overline{T} →\overline{T} then fix(f) = fix(f′) ∈ \overline{T}.

Proof \overline{T} type since \overline{T} → \overline{T} type. Note that f^{j} = f^{′j} ∈ \overline{T} for every j. Suppose fix(f)↓. By compactness, f^{j}↓ for some j. Since f^{j} = f^{′j} ∈ \overline{T}, it follows that f^{′j}↓ and thus fix(f′)↓ by Corollary 5.6. Similarly fix(f′)↓ implies fix(f)↓. It remains to show that fix(f) = fix(f′) ∈ T when fix(f)↓. Suppose again that fix(f)↓. As before, there exists j such that f^{j}↓ by compactness. Hence f^{j} = f^{′j} ∈ T. Since T is admissible, fix(f) = fix(f′) ∈ T. □

A number of closure conditions exist on admissible types and are given in Lemma 5.15. Informally, basic compound types other than dependent products are admissible so long as their component types in positive positions are admissible. Base types—natural numbers, convergence types, and (for this lemma only) equality types—are always admissible. These are essentially the admissible types of Smith [92], except that for a function type to be admissible Smith required that its domain type be admissible.

Lemma 5.15

• Adm(A + B) if Adm(A) and Adm(B)

• Adm(Πx:A.B) if ∀a ∈ A. Adm(B[a/x])

• Adm(A × B) if Adm(A) and Adm(B)

• Adm(Void), Adm(Atom), Adm(ℤ) and Adm(𝔼)

• Adm(a = a′ in A)

• Adm(a ≤ a′)

• Adm(\overline{A}) if Adm(A)

• Adm(a in! A)

Proof The proof follows the same lines as Smith’s proof, except that handling equality adds a small amount of complication to the proof. I show the function case by way of example.

Let f, t and t′ be arbitrary. Suppose j is such that ∀k ≥ j. t^{[k]} = t^{′[k]} ∈ Πx:A.B. I need to show that t^{[ω]} = t^{′[ω]} ∈ Πx:A.B. Since Πx:A.B is inhabited it is a type. Both t^{[j]} and t^{′[j]} converge to lambda abstractions, so, by Corollary 5.6, t^{[ω]} ⇓ λx.b and t^{′[ω]} ⇓ λx.b′ for some terms b and b′. Suppose a = a′ ∈ A. To get that b[a/x] = b′[a′/x] ∈ B[a/x] it suffices to show that t^{[ω]}a = t^{′[ω]}a′ ∈ B[a/x]. Since Adm(B[a/x]), it suffices to show that ∀k ≥ j. t^{[k]}a = t^{′[k]}a′ ∈ B[a/x], which follows from the supposition. □

Unfortunately, Lemma 5.15 can show the admissibility of a product space only if it is non-dependent. Dependent products do not have an admissibility condition similar to that of dependent functions. This reason for this is as follows: Admissibility states that a single fixed type contains the limit of an approximation chain if it contains a cofinite subset of that chain. For functions, disjoint union, partial types, and non-dependent products it is possible to decompose prospective members in such a way that admissibility may be applied to a single type (such as the type B[a/x] used in the proof of Lemma 5.15). In contrast, for a dependent product, the right-hand term’s desired type depends upon the left-hand term, which is changing at the same time as the right-hand term. Consequently, there is no single type into which to place the right-hand term.

However, understanding the problem with dependent products suggests a solution, to generalize the definition of admissibility to allow the type to vary. This leads to the notion of predicate-admissibility that I discuss in the next section.

5.3.1 Predicate-Admissibility

Definition 5.16 A type T is predicate-admissible for x in S (abbreviated Adm(T | x : S)) if:

∀f, t, t′, e. e^{[ω]} ∈ S ∧ (∃j. ∀k ≥ j. e^{[k]} ∈ S ∧ t^{[k]} = t^{′[k]} ∈ T [e^{[k]}/x]) ⇒ t^{[ω]} = t^{′[ω]} ∈ T [e^{[ω]}/x]

The term “predicate-admissibility” stems from its similarity to the notion of admissibility of predicates in domain theory (and LCF). If one ignores the inhabiting terms t and t′, which may be seen as evidences of the truth of the predicate T [ ], then predicate-admissibility is saying T [e^{[ω]}] if T [e^{[k]}] for all k greater than some j. This is precisely the notion of admissibility of predicates in domain theory. Indeed, the results here were influenced by the work of Igarashi [55], who established conditions on admissibility of domain-theoretic predicates.

To show the admissibility of a dependent product type, it is sufficient to show predicateadmissibility of the right-hand side (along with admissibility of the left):

Lemma 5.17 The type Σx:A.B is admissible if Adm(A) and Adm(B | x : A).

Proof Let f, t and t′ be arbitrary. Suppose j is such that ∀k ≥ j. t^{[k]} = t^{′[k]} ∈ Σx:A.B. It is necessary to show that t^{[ω]} = t^{′[ω]} ∈ Σx:A.B. Since Σx:A.B is inhabited it is a type. Both t^{[j]} and t^{′[j]} converge to pairs, so, by Corollary 5.6, t^{[ω]} ⇓ ⟨a, b⟩ and t^{′[ω]} ⇓ ⟨a′, b′⟩ for some terms a, b, a′ and b′. To get that a = a′ ∈ A it suffices to show that π_{1}(t^{[ω]}) = π_{1}(t^{′[ω]}) ∈ A. Since Adm(A), it suffices to show that ∀k ≥ j. π_{1}(t^{[k]}) = π_{1}(t^{′[k]}) ∈ A, which follows from the supposition.

To get that b = b′ ∈ B[a/x] (the interesting part), it suffices to show that π_{2}(t^{[ω]}) = π_{2}(t^{′[ω]}) ∈ B[π_{1}(t^{[ω]})/x]. Since Adm(B | x : A), it suffices to show that π_{1}(t^{[ω]}) ∈ A, which has already been shown, and ∀k ≥ j. π_{1}(t^{[k]}) ∈ A ∧ π_{2}(t^{[k]}) = π_{2}(t^{′[k]}) ∈ B[π_{1}(t^{[k]})/x], which follows from the supposition. □

The conditions for predicate-admissibility are more elaborate, but also more general. I may immediately state conditions for basic types other than functions. Informally, basic compound types other than functions are predicate-admissible so long as their component types are predicate-admissible, and base types are always predicate-admissible.

Lemma 5.18

• Adm(A + B | y : S) if ∀s ∈ S. (A + B)[s/y] type and Adm(A | y : S) and Adm(B | y : S).

• Adm(Σx:A.B | y : S) if ∀s ∈ S. (Σx:A.B)[s/y] type and Σy:S.A type and Adm(A | y : S) and Adm(B[π_{1}(z), π_{2}(z)/y, x] | z : (Σy:S.A))

• Adm(Void | y : S), Adm(Atom | y : S), Adm(ℤ | y : S) and Adm(𝔼 | y : S)

• Adm(a_{1} = a_{2} in A | y : S) if ∀s ∈ S. (a_{1} = a_{2} in A)[s/y] type and Adm(A | y : S)

• Adm(a_{1} ≤ a_{2} | y : S)

• Adm(\overline{A} | y : S) if ∀s ∈ S. \overline{A}[s/y] type and Adm(A | y : S)

• Adm(a in! A | y : S) if ∀s ∈ S. (a in! A)[s/y] type

Predicate-admissibility of a function type is more complicated because a function argument with the type A[e^{[ω]}/x] does not necessarily belong to any of the finite approximations A[e^{[j]}/x]. To settle this, it is necessary to require a coadmissibility condition on the domain type. Then a function type will be predicate-admissible if the domain is weakly coadmissible and the codomain is predicate-admissible.

Definition 5.19 A type T is weakly coadmissible for x in S (abbreviated WCoAdm(T | x : S)) if:

\begin{matrix}∀f, t, t′, e. e^{[ω]} ∈ S ∧ (∃j. ∀k ≥ j. e^{[k]} ∈ S) ∧ t = t′ ∈ T [e^{[ω]}/x] ⇒ \\ (∃j. ∀k ≥ j. t = t′ ∈ T [e^{[k]}/x]) \end{matrix}

A type T is coadmissible for x in S (abbreviated CoAdm(T | x : S)) if:

\begin{matrix}∀f, t, t′, e. e^{[ω]} ∈ S ∧ (∃j. ∀k ≥ j. e^{[k]} ∈ S) ∧ t^{[ω]} = t^{′[ω]} ∈ T [e^{[ω]}/x] ⇒ \\ (∃j. ∀k ≥ j. t^{[k]} = t^{′[k]} ∈ T [e^{[k]}/x]) \end{matrix}

Lemma 5.20 Adm(Πx:A.B | y : S) if ∀s ∈ S. (Σx:A.B)[s/y] type and WCoAdm(A | y : S) and ∀s ∈ S, a ∈ A[s/y]. Adm(B[a/x] | y : S)

Clearly coadmissibility implies weak coadmissibility. A general set of conditions listed in Lemma 5.21 establish weak and full coadmissibility for various types. Weak and full coadmissibility are closed under disjoint union and dependent sum formation, and full coadmissibility is additionally closed under equality-type formation. I use both notions of coadmissibility, rather than just adopting one or the other, because full coadmissibility is needed for equality types but under certain circumstances weak coadmissibility is easier to show (Proposition 5.22 below).

Lemma 5.21

• A + B is (weakly) coadmissible for y in S if ∀s ∈ S. (A + B)[s/y] type and A and B are (weakly) coadmissible for y in S

• WCoAdm(Σx:A.B | y : S) if ∀s ∈ S. (Σx:A.B)[s/y] type and WCoAdm(A | y : S) and ∀s ∈ S, a ∈ A[s/y]. WCoAdm(B[a/x] | y : S)

• CoAdm(Σx:A.B |y : S) if ∀s ∈ S. (Σx:A.B)[s/y] type and Σy:S.A type and CoAdm(A|y : S) and CoAdm(B[π_{1}(z), π_{2}(z)/y, x] | z : (Σy:S.A))

• Void, Atom, ℤ and 𝔼 are strongly or weakly coadmissible for y in any S

• CoAdm(a_{1} = a_{2} in A | y : S) if ∀s ∈ S. (a_{1} = a_{2} in A)[s/y] type and CoAdm(A | y : S)

• a_{1} ≤ a_{2} is strongly or weakly coadmissible for y in any S

• \overline{A} is (weakly) coadmissible for y in S if ∀s ∈ S. \overline{A}[s/y] type and A is (weakly) coadmissible for y is S • a in! A is strongly or weakly coadmissible for y in S if ∀s ∈ S. (a in! A)[s/y] type

When T does not depend upon S, predicate-admissibility and weak coadmissibility become easier to show:

Proposition 5.22 Suppose x does not appear free in T. Then:

• Adm(T) if Adm(T | x : S) and S is inhabited

• Adm(T | x : S) if Adm(T)

• WCoAdm(T | x : S)

There remains one more result related to predicate-admissibility. Suppose one wishes to show Adm(T | x : S) where T depends upon x. There are two ways that x may be used in T. First, T might contain an equality type where x appears in one or both of the equands. In that case, predicate-admissibility can be shown with the tools discussed above. Second, T may be an expression that computes a type from x. In this case, T can be simplified using direct computation (Section 4.1.7), but another tool will be needed if T performs any case analysis (as in the embedding of the λ^{K} disjoint union type in Section 3.3.1).

Lemma 5.23 Consider a type case(d, x.A, x.B) that depends upon y from S. Suppose there exist T_{1} and T_{2} such that:

• ∀s ∈ S. d[s/y] ∈ (T_{1} + T_{2})[s/y]

• ∀s ∈ S, t ∈ T_{1}[s/y]. A[s, t/y, x] type

• ∀s ∈ S, t ∈ T_{2}[s/y]. B[s, t/y, x] type

• Σy:S.T_{1} type and Σy:S.T_{2} type

Then the following are the case:

• Adm(case(d, x.A, x.B) | y : S) if Adm(A[π_{1}(z), π_{2}(z)/y, x] | z : (Σy:S.T_{1})) and Adm(B[π_{1}(z), π_{2}(z)/y, x] | z : (Σy:S.T_{2}))

• WCoAdm(case(d, x.A, x.B) | y : S) if WCoAdm(A[π_{1}(z), π_{2}(z)/y, x] | z : (Σy:S.T_{1})) and WCoAdm(B[π_{1}(z), π_{2}(z)/y, x] | z : (Σy:S.T_{2}))

• CoAdm(case(d, x.A, x.B) | y : S) if CoAdm(A[π_{1}(z), π_{2}(z)/y, x] | z : (Σy:S.T_{1})) and CoAdm(B[π_{1}(z), π_{2}(z)/y, x] | z : (Σy:S.T_{2}))

5.3.2 Monotonicity

In some cases a very simple device may be used to show admissibility. We say that a type is monotone if it respects computational approximation, and it is easy to show that all monotone types are admissible.

Definition 5.24 A type T is monotone (abbreviated Mono(t)) if t = t′ ∈ T whenever t ∈ T and t ≤ t′.

Lemma 5.25 All monotone types are admissible.

Proof

Let f, t and t′ be arbitrary and suppose there exists j such that t^{[j]} = t^{′[j]} ∈ T. Since t^{[j]} ≤ t^{[ω]} and t^{′[j]} ≤ t^{′[ω]}, it follows that t^{[j]} = t^{[ω]} ∈ T and t^{′[j]} = t^{′[ω]} ∈ T. The result follows directly. □

All type constructors are monotone except universes and partial types, which are never monotone. The proof of this fact is easy [53].

Proposition 5.26

• Mono(A + B) if Mono(A) and Mono(B)

• Mono(Πx:A.B) if Mono(A) and ∀a ∈ A. Mono(B[a/x])

• Mono(Σx:A.B) if Mono(A) and ∀a ∈ A. Mono(B[a/x])

• Mono(Void), Mono(Atom), Mono(ℤ), Mono(𝔼), Mono(a_{1} = a_{2} ∈ A), Mono(a_{1} ≤ a_{2}) and Mono(a in! A)

5.3.3 Set and Quotient Types

Given the parallel between the dependent product and set types, it is natural to expect that set types would have a similar admissibility rule: that {x : A | B} if A is admissible and B is predicate-admissible for x in A. Surprisingly, this turns out not to be the case. Suppose that t^{[k]} ∈ {x : A | B} for all k ≥ j. Then for every k ≥ j, there exists some term b_{k} ∈ B[t^{[k]}/x]. We would like it to follow by predicate-admissibility that there exists b_{ω} ∈ B[t^{[ω]}/x], but it does not. The problem is that each b_{k} can be a completely different term, and predicate-admissibility applies only when each b_{k} is of the form b[f^{k}/w] for a single fixed b.

Intuitively, the desired rule fails because the set type {x : A|B} suppresses the computational content of B and therefore B can be inhabited non-uniformly, by unrelated terms for related members of A. In contrast, if the chain t^{[j]}, t^{[j+1]}, t^{[j+2]}, … belongs to Σx:A.B, then the chain π_{2}(t)^{[j]}, π_{2}(t)^{[j+1]}, π_{2}(t)^{[j+2]}, … uniformly inhabits B.

For a concrete example, consider:

T =^{def} \{g : ℤ → \overline{Unit} | ∃n:ℤ. ¬(gn in! ℤ)\}
f =^{def} λh. λx. if x ≤_{Z} 0 then ⋆ else h(x − 1)
t =^{def} λy. wy

The type T is not admissible: For all k, (t^{[k]_{f}})k diverges, so t^{[k]_{f}} ∈ T; but t^{[ω]_{f}} converges for all arguments, so t^{[ω]_{f}} ∉ T. However, ∃n:ℤ. ¬(gn in! ℤ) is predicate-admissible for g in ℤ → \overline{Unit}. The problem is that the inhabiting integers are not related by computational approximation; that is, they are not uniform.

To show a set type admissible, we need to be able to show that the selection predicate can be inhabited uniformly:

Lemma 5.27 The type {x : A | B} is admissible if:

• Adm(A), and • Adm(B | x : A), and

• there exists b such that b[a/x] ∈ B[a/x] whenever a ∈ A and ∃b′. b′ ∈ B[a/x]. Another way to state the uniformity condition (the third clause) is that the computational content of B should be recoverable from knowing it exists (i.e., there exists a function with type Πx:A. ↓B→B. In fact, the two versions are equivalent modulo a few functionality requirements: Proposition 5.28

• If f ∈ Πx:A. ↓B → B then b[a/x] ∈ B[a/x] whenever a ∈ A and ∃b′. b′ ∈ B[a/x], where b = f x ⋆.

• If b[a/x] = b[a′/x] ∈ B[a/x] whenever a = a′ ∈ A and ∃b′. B[a/x] and if B[a/x] = B[a′/x]

whenever a = a′ ∈ A then λx.λy. b ∈ Πx:A. ↓B → B. We may give a similar predicate-admissibility condition: Lemma 5.29 Adm({x : A | B} | y : S) if ∀s ∈ S. ({x : A | B})[s/y] type and Σy:S.A type and Adm(A | y : S) and Adm(B[π_{1}(z), π_{2}(z)/y, x] | z : Σy:S.A), and there exists b such that b[a, s/x, y] ∈ B[a, s/x, y] whenever s ∈ S and a ∈ A[s/y] and ∃b′. b′ ∈ B[a, s/x, y] Coadmissibility and monotonicity work on single terms, not chains, so the uniformity issue does not arise, resulting in conditions fairly similar to those for dependent products: Lemma 5.30

• WCoAdm({x : A | B} | y : S) if ∀s ∈ S. {x : A | B}[s/y] type and WCoAdm(A | y : S) and ∀s ∈ S, a ∈ A[s/y]. WCoAdm(B[a/x] | y : S)

• CoAdm({x : A | B} | y : S) if ∀s ∈ S. {x : A | B}[s/y] type and Σy:S.A type and CoAdm(A | y : S) and WCoAdm(B[π_{1}(z), π_{2}(z)/y, x] | z : (Σy:S.A))

• Mono({x : A | B}) if Mono(A) The conditions for quotient types are similar to those for set types: Lemma 5.31

• Adm(xy:A//B) if Adm(A) and Adm(B[π_{1}(z), π_{2}(z)/x, y] | z : A × A), and there exists b such that b[a, a′/x, y] ∈ B[a, a′/x, y] whenever a ∈ A and a′ ∈ A and ∃b′. b′ ∈ B[a, a′/x, y].

• Adm(xy:A//B|z : S) if ∀s ∈ S.(xy:A//B)[s/z]type and Σz:S.(A×A)type and Adm(A|z : S) and Adm(B[π_{1}(z′), π_{1}(π_{2}(z′)), π_{2}(π_{2}(z′))/z, x, y] | z′ : Σz:S.(A × A)), and there exists b such that b[a, a′, s/x, y, z] ∈ B[a, a′, s/x, y, z] whenever s ∈ S and a ∈ A[s/z] and a′ ∈ A[s/z] and ∃b′. b′ ∈ B[a, a′, s/x, y, z].

• WCoAdm(xy:A//B | z : S) if ∀s ∈ S. (xy:A//B)[s/z] type and WCoAdm(A | z : S) and ∀s ∈ S, a ∈ A[s/z], a′ ∈ A[s/z]. WCoAdm(B[a, a′/x, y] | z : S)

• CoAdm(xy:A//B | z : S) if ∀s ∈ S. (xy:A//B)[s/z] type and Σz:S.(A × A) type and CoAdm(A | z : S) and CoAdm(B[π_{1}(z′), π_{1}(π_{2}(z′)), π_{2}(π_{2}(z′))/z, x, y] | z′ : Σz:S.(A × A))

• Mono(xy:A//B) if Mono(A)

5.3.4 Summary

Figure 5.1 provides a summary of the basic admissibility results of this chapter. It is worthwhile to note that all these results are proved constructively, with the exception of (weak and full) coadmissibility of partial types. The following theorem shows that the proofs of coadmissibility of partial types are necessarily classical; if a constructive proof existed then one could extract an algorithm meeting the theorem’s specification, which can be used to solve the halting problem.

Theorem 5.32 There does not exist an algorithm that computes an integer j such that ∀k ≥ j. t = t′ ∈ \overline{T} [e^{[k]}/x], when given S, T, f, t, t′, e and i such that:

• ∀s ∈ S. \overline{T} [s/x] type

• CoAdm(T | x : S)

• e^{[ω]} ∈ S

• ∀k ≥ i. e^{[k]} ∈ S

• t = t′ ∈ \overline{T} [e^{[ω]}/x]

Recall the inadmissible type T from Theorem 5.1. That type fails the predicate-admissibility condition because of the negative appearance of a function type, which could not be shown weakly coadmissible, and it fails the monotonicity condition because it contains the partial type \overline{ℕ}.

5.4 Conclusions

An interesting avenue for future investigation would be to find some negative results characterizing inadmissible types. Such negative results would be particularly interesting if they could be given a syntactic character, like the results of this chapter. Along these lines, it would be interesting to find whether the inability to show coadmissibility of function types represents a weakness of this proof technique or an inherent limitation.

The results presented above provide metatheoretical justification for the fixpoint principle over many types. In order for these results to be useful in theorem proving, they must be introduced into the logic. One way to do this, and the way it is done here, is to introduce types to represent the assertions Adm(T), Adm(T | x : S), etc., that are inhabited exactly when the underlying assertion is true (in much that same way as the equality type is inhabited exactly when the equands are equal), and to add rules relating to these types that correspond to the lemmas of Section 5.3. This brings the tools into the system in a semantically justifiable way, but it is unpleasant in that it leads to a proliferation of new types and inference rules stemming from discoveries outside the logic. Of the Nuprl proof rules of Appendix B, 84 rules (not quite half) deal with admissibility. It would be preferable to deal with admissibility within the logic. A theory with intensional reasoning principles, such as the one proposed in Constable and Crary [23], would allow reasoning about computation internally. Then these results could be proved within the theory and the only extra rule that would be required would be a single rule relating admissibility to the the fixpoint principle.

However they are placed into the logic, these results allow for recursive computation on a wide variety of types. This make partial types and fixpoint induction a useful tool in typetheoretic theorem provers. It also makes it possible to study many recursive programs that used to be barred from the logic because they could not be typed.

For T ≡
A + B Πx:A.B Σx:A.B

Adm(T) if

T type and Adm(A) ∧ Adm(B) ∀a ∈ A. Adm(B[a/x]) Adm(A) ∧ Adm(B | x : A)
Adm(A | y : S) ∧
WCoAdm(A | y : S) ∧ Adm(B[π_{1}(z), π_{2}(z)/y, x]
Adm(T | y : S) if Adm(A | y : S) ∧ ∀s ∈ S, a ∈ A[s/y]. | z : (Σy : S.A)) ∧
∀s ∈ S. T [s/y] type and Adm(B | y : S)  Adm(B[a/x] | y : S) Σy:S.A type
WCoAdm(A | y : S) ∧
WCoAdm(T | y : S) if  WCoAdm(A | y : S) ∧  ∀s ∈ S, a ∈ A[s/y].
∀s ∈ S. T [s/y] type and WCoAdm(B | y : S)  —  WCoAdm(B[a/x] | y : S)
CoAdm(A | y : S) ∧
CoAdm(B[π_{1}(z), π_{2}(z)/y, x]
CoAdm(T | y : S) if  CoAdm(A | y : S) ∧  | z : (Σy : S.A)) ∧
∀s ∈ S. T [s/y] type and CoAdm(B | y : S)  —  Σy:S.A type
Mono(A) ∧  Mono(A) ∧
Mono(T) if  Mono(A) ∧ Mono(B) ∀a ∈ A. Mono(B[a/x]) ∀a ∈ A. Mono(B[a/x])
For T ≡
Void, Atom, ℤ, 𝔼, a_{1} ≤ a_{2}, a in! A a_{1} = a_{2} in A \overline{A}

Adm(T) if

T type and yes yes Adm(A)

Adm(T | y : S) if

∀s ∈ S. T [s/y] type and yes Adm(A | y : S) Adm(A | y : S)

WCoAdm(T | y : S) if

∀s ∈ S. T [s/y] type and yes  CoAdm(A | y : S) WCoAdm(A | y : S)

CoAdm(T | y : S) if

∀s ∈ S. T [s/y] type and yes  CoAdm(A | y : S) CoAdm(A | y : S)
Mono(T) if  yes  yes  —

Figure 5.1: Admissibility, coadmissibility and monotonicity conditions

Conclusion

In this dissertation I have explored how to draw formal connections between type theory and practical programming languages. These connections allow the full power of foundational type theory to be brought to bear on research problems arising from real programs and programming languages.

The least consequence of these contributions is that program verification results in typetheoretic systems may be directly applied to real programs, without trusting an intermediate hand translation into type theory. More significantly, type theory may be applied to programming languages as a whole; this allows us to understand programming languages within a rich mathematical framework, but one that is inherently computational and that also does not disturb the essential structure of programs.

Three main technical achievements made this possible. First, I presented a semantics for a practical programming calculus in the framework of the Nuprl type theory. This semantics was stated as a syntax-directed embedding of the constructs, and used novel type-theoretic mechanisms to address the language mechanisms of recursion, a rich dependent kind structure, and a higher-order modules system with translucent signatures.

Second, backing up the type-theoretic semantics was the first constructive type theory to include an intrinsic notion of equality, and to support reasoning about partial and total functions. All three are necessary to allow interesting reasoning about real programs. Partiality is necessary because real programs often make use of recursion. However, a notion of totality is also necessary to allow reasoning about pure values, which are central to real programs as well.

Third, broad new techniques for showing admissibility of types for fixpoint induction were necessary for each of the previous contributions. Although the techniques I present are still conservative, they make possible admissibility proofs for many types that could not be shown admissible by previous techniques. For example, types arising in the embedding of disjoint unions (Section 3.3.1) and in fixpoint induction (Section 4.3.3) required the new techniques to be shown admissible.

The broadest aim of this work has been to bring theory and practice closer together. Aside from the specific contributions that make up this dissertation, I have found that an important way this work has helped achieve that is as a conceptual tool for thinking about programming and programming languages. Practical languages are made up of diverse sets of often complicated mechanisms. In contrast, languages for theoretical study usually are made up of small sets of elegant mechanisms. Type theory, like other theoretical languages, is made up of elegant mechanisms, but I have shown in this dissertation that those mechanisms are sufficiently powerful to account for most of the diverse mechanisms of practical languages, and those that remain show great promise of being addressed as well (recall Section 3.5).

Consequently, I have found it very profitable to use type theory as a tool for considering general programming issues. The power and elegance of type theory allow a researcher to derive elegant solutions to diverse problems but do not require one to ignore practical issues to get them.

Appendix A

Lambda-K Typing Rules

Level Definitions

level(Type_{i}) =^{def} i
level(P_{i}(c)) =^{def} i
level(S_{i}(c)) =^{def} i

level(Πα:κ_{1}.κ_{2}) =^{def} max(level(κ_{1}), level(κ_{2}))

level({ℓ_{i} ▷ α_{i} : κ_{i}^{[i=1...n]}}) =^{def} max_{i=1}^{n} level(κ_{i})

Kind Formation and Equality

{Γ ⊢_{K} κ_{2} = κ_{1} \atop Γ ⊢_{K} κ_{1} = κ_{2}}  (KindEqSymm)

\begin{matrix}Γ ⊢_{K} κ_{1} = κ_{2} & Γ ⊢_{IL} κ_{2} = κ_{3} \\ Γ ⊢_{K} κ_{1} = κ_{3} \end{matrix} (KindEqTrans)

Γ ⊢_{K} Type_{i} = Type_{i}  (Type)

\begin{matrix}Γ ⊢_{K} κ_{1} = κ′_{1} & Γ[α : κ_{1}] ⊢_{K} κ_{2} = κ′_{2} \\ Γ ⊢_{K} Πα:κ_{1}.κ_{2} = Πα:κ′_{1}.κ′_{2} \end{matrix} (α ∉ Γ)

(Pi)

{Γ[α_{j} : κ_{j}]^{[j=1...i−1]} ⊢_{K} κ_{i} = κ′_{i} for 1 ≤ i ≤ n \atop Γ ⊢_{K} \{ℓ_{i} ▷ α_{i} : κ_{i}^{[i=1...n]}\} = \{ℓ_{i} ▷ α_{i} : κ′_{i}^{[i=1...n]}\}} ; (α_{i}^{[i=1...m]} ∉ Γ)

(DepRecord)

{Γ ⊢_{K} c_{1} = c_{2} : Type_{i} \atop Γ ⊢_{K} P_{i}(c_{1}) = P_{i}(c_{2})}  (Pow)

{Γ ⊢_{K} c_{1} = c_{2} : Type_{i} \atop Γ ⊢_{K} S_{i}(c_{1}) = S_{i}(c_{2})}  (Sing)

Subkinding

{Γ ⊢_{K} κ_{1} = κ_{2} \atop Γ ⊢_{K} κ_{1} ⪯ κ_{2}}  (SubkindReflex)

\begin{matrix}Γ ⊢_{K} κ_{1} ⪯ κ_{2} & Γ ⊢_{IL} κ_{2} ⪯ κ_{3} \\ Γ ⊢_{K} κ_{1} ⪯ κ_{3} \end{matrix} (SubkindTrans)

Γ ⊢_{K} Type_{i} ⪯ Type_{j} (i ≤ j)  (TypeSub)

\begin{matrix}Γ ⊢_{K} κ′_{1} ⪯ κ_{1} \\ Γ[α : κ′_{1}] ⊢_{K} κ_{2} ⪯ κ′_{2} \\ Γ[α : κ_{1}] ⊢_{K} κ_{2} kind \end{matrix} Γ ⊢_{K} Πα:κ_{1}.κ_{2} ⪯ Πα:κ′_{1}.κ′_{2} (α ∉ Γ) (PiSub)

Γ[α_{j} : κ_{j}]^{[j=1...i−1]} ⊢_{K} κ_{i} ⪯ κ′_{i} for 1 ≤ i ≤ n Γ[α_{j} : κ_{j}]^{[j=1...i−1]} ⊢_{K} κ_{i} kind for n < i ≤ m Γ[α_{j} : κ′_{j}]^{[j=1...i−1]} ⊢_{K} κ′_{i} kind for 1 ≤ i ≤ n

Γ ⊢_{K} {ℓ_{i} ▷ α_{i} : κ_{i}^{[i=1...m]}} ⪯ {ℓ_{i} ▷ α_{i} : κ′_{i}^{[i=1...n]}}

(α_{i}^{[i=1...m]} ∉ Γ, m ≥ n)

(DepRecordSub)

{Γ ⊢_{K} c_{1} ⪯ c_{2} \atop Γ ⊢_{K} P_{i}(c_{1}) ⪯ P_{j}(c_{2})} (i ≤ j)  (PowSub)
Γ ⊢_{K} c : Type_{i}
Γ ⊢_{K} P_{i}(c) ⪯ Type_{i}

(PowType)

{Γ ⊢_{K} c_{1} = c_{2} : Type_{i} \atop Γ ⊢_{K} S_{i}(c_{1}) ⪯ S_{j}(c_{2})} (i ≤ j)  (SingSub)

{Γ ⊢_{K} c : Type_{i} \atop Γ ⊢_{K} S_{i}(c) ⪯ P_{i}(c)}  (SingPow)

Constructor Formation and Equality

{Γ ⊢_{K} c_{2} = c_{1} : κ \atop Γ ⊢_{K} c_{1} = c_{2} : κ}  (EqSymm)

\begin{matrix}Γ ⊢_{K} c_{1} = c_{2} : κ & Γ ⊢_{K} c_{2} = c_{3} : κ \\ Γ ⊢_{K} c_{1} = c_{3} : κ \end{matrix} (EqTrans)

Γ ⊢_{K} α = α : κ (Γ(α) = κ)  (TypeVar)
Γ ⊢_{K} κ_{1} kind Γ[α : κ_{1}] ⊢_{K} c = c′ : κ_{2}

Γ ⊢_{K} λα:κ_{1}.c = λα:κ_{1}.c′ : Πα:κ_{1}.κ_{2} (α ∉ Γ)

(PiIntro)

\begin{matrix}Γ ⊢_{K} c_{1} = c′_{1} : Πα:κ_{1}.κ_{2} & Γ ⊢_{K} c_{2} = c′_{2} : κ_{1} \\ Γ ⊢_{K} c_{1}[c_{2}] = c′_{1}[c′_{2}] : κ_{2}[c_{2}/α] \end{matrix}

(PiElim)

\begin{matrix}Γ ⊢_{K} c_{i} = c′_{i} : κ_{i}[c_{j}^{[j=1...i−1]}/α_{j}^{[j=1...i−1]}] \\ (for 1 ≤ i ≤ n) \end{matrix}

Γ ⊢_{K} {ℓ_{i} ▷ α_{i} : κ_{i}^{[i=1...n]}} kind

\begin{matrix}Γ ⊢_{K} \{ℓ_{i} = c_{i}^{[i=1...n]}\} = \{ℓ_{i} = c′_{i}^{[i=1...n]}\} : \\ \{ℓ_{i} ▷ α_{i} : κ_{i}^{[i=1...n]}\} \end{matrix}

(DepRecordIntro)

{Γ ⊢_{K} c = c′ : \{ℓ_{j} ▷ α_{j} : κ_{j}^{[i=1...n]}\} \atop Γ ⊢_{K} \begin{matrix}π_{ℓ_{i}}(c) = π_{ℓ_{i}}(c′) : \\ κ_{i}[π_{ℓ_{j}}(c)^{[j=1...i−1]}/α_{j}^{[j=1...i−1]}] \end{matrix}} ; (1 ≤ i ≤ n)

(DepRecordElim)

Γ ⊢_{K} c_{1} = c′_{1} : Type_{i} Γ ⊢_{K} c_{2} = c′_{2} : Type_{i}
Γ ⊢_{K} c_{1} → c_{2} = c′_{1} → c′_{2} : Type_{i}

(Arrow)

Γ ⊢_{K} c_{1} = c′_{1} : Type_{i} Γ ⊢_{K} c_{2} = c′_{2} : Type_{i}
Γ ⊢_{K} c_{1} ⇒ c_{2} = c′_{1} ⇒ c′_{2} : Type_{i}

(TArrow)

Γ ⊢_{K} κ_{1} = κ_{2} Γ[α : κ_{1}] ⊢_{K} c = c′ : Type_{i}
Γ ⊢_{K} ∀α:κ_{1}.c_{1} = ∀α:κ_{2}.c_{2} : Type_{i}
(α ∉ Γ, level(κ_{1}) < i)

(Quant)

Γ ⊢_{K} c_{i} = c′_{i} : Type_{j} for 1 ≤ i ≤ n

Γ ⊢_{K} \begin{matrix}\{ℓ_{1} : c_{1}, …, ℓ_{n} : c_{n}\} = \\ \{ℓ_{1} : c′_{1}, …, ℓ_{n} : c′_{n}\} : Type_{j} \end{matrix} (Record)

Γ ⊢_{K} c_{i} = c′_{i} : Type_{j} for 1 ≤ i ≤ n

Γ ⊢_{K} \begin{matrix}⟨ℓ_{1} : c_{1}, …, ℓ_{n} : c_{n}⟩ = \\ ⟨ℓ_{1} : c′_{1}, …, ℓ_{n} : c′_{n}⟩ : Type_{j} \end{matrix} (Union)

\begin{matrix}Γ ⊢_{K} c = c′ : κ_{1} & Γ ⊢_{K} κ_{1} ⪯ κ_{2} \\ Γ ⊢_{K} c = c′ : κ_{2} \end{matrix}  (Subkind)

{Γ ⊢_{K} (λα:κ′.c_{1})[c_{2}] : κ \atop Γ ⊢_{K} (λα:κ′.c_{1})[c_{2}] = c_{1}[c_{2}/α] : κ} (PiBeta)

{Γ ⊢_{K} λα:κ′_{1}.c[α] : Πα:κ_{1}.κ_{2} \atop Γ ⊢_{K} λα:κ′_{1}.c[α] = c : Πα:κ_{1}.κ_{2}} (α ∉ c) (PiEta)

{Γ ⊢_{K} π_{ℓ_{j}}(\{ℓ_{i} = c_{i}^{[i=1...n]}\}) : κ \atop Γ ⊢_{K} π_{ℓ_{j}}(\{ℓ_{i} = c_{i}^{[i=1...n]}\}) = c_{j} : κ} (1 ≤ j ≤ n)

(DepRecordBeta)

{Γ ⊢_{K} \{ℓ_{i} = π_{ℓ_{i}}(c)^{[i=1...n]}\} : \{ℓ_{i} ▷ α_{i} : κ_{i}^{[i=1...n]}\} \atop Γ ⊢_{K} \{ℓ_{i} = π_{ℓ_{i}}(c)^{[i=1...n]}\} = c : \{ℓ_{i} ▷ α_{i} : κ_{i}^{[i=1...n]}\}}

(DepRecordEta)

{Γ ⊢_{K} c_{1} = c_{2} : Type_{i} \atop Γ ⊢_{K} c_{1} : S_{i}(c_{2})}  (SingIntro)
{Γ ⊢_{K} c_{1} : S_{i}(c_{2}) \atop Γ ⊢_{K} c_{1} = c_{2} : Type_{i}}  (SingElim)

\begin{matrix}Γ ⊢_{K} c_{1} = c_{2} : Type_{i} & Γ ⊢_{K} c_{1} : P_{i}(c_{3}) \\ Γ ⊢_{K} c_{1} = c_{2} : P_{i}(c_{3}) \end{matrix} (PowFun)

\begin{matrix}Γ ⊢_{K} c_{1} = c_{2} : Type_{i} & Γ ⊢_{K} c_{1} : S_{i}(c_{3}) \\ Γ ⊢_{K} c_{1} = c_{2} : S_{i}(c_{3}) \end{matrix} (SingFun)
{Γ ⊢_{K} m : ⟨κ⟩ \atop Γ ⊢_{K} ext(m) : κ}  (TypeModElim)

Subtyping

\begin{matrix}Γ ⊢_{K} c_{1} ⪯ c_{2} & Γ ⊢_{K} c_{2} ⪯ c_{3} \\ Γ ⊢_{K} c_{1} ⪯ c_{3} \end{matrix}  (SubTrans)
\begin{matrix}Γ ⊢_{K} c′_{1} ⪯ c_{1} & Γ ⊢_{K} c_{2} ⪯ c′_{2} \\ Γ ⊢_{K} c_{1} → c_{2} ⪯ c′_{1} → c′_{2} \end{matrix}  (ArrowSub)
\begin{matrix}Γ ⊢_{K} c′_{1} ⪯ c_{1} & Γ ⊢_{K} c_{2} ⪯ c′_{2} \\ Γ ⊢_{K} c_{1} ⇒ c_{2} ⪯ c′_{1} ⇒ c′_{2} \end{matrix}  (TArrowSub)
Γ ⊢_{K} c_{1} type Γ ⊢_{K} c_{2} type

Γ ⊢_{K} c_{1} ⇒ c_{2} ⪯ c_{1} → c_{2} (TotalSub)

\begin{matrix}Γ ⊢_{K} κ_{2} ⪯ κ_{1} \\ Γ[α : κ_{2}] ⊢_{K} c_{1} ⪯ c_{2} \\ Γ[α : κ_{1}] ⊢_{K} c_{1} type \end{matrix} Γ ⊢_{K} ∀α:κ_{1}.c_{1} ⪯ ∀α:κ_{2}.c_{2} (α ∉ Γ) (QuantSub)

Γ ⊢_{K} c_{i} ⪯ c′_{i} for 1 ≤ i ≤ n Γ ⊢_{K} c_{i} type for n < i ≤ m

Γ ⊢_{K} \begin{matrix}\{ℓ_{1} : c_{1}, …, ℓ_{n} : c_{m}\} ⪯ \\ \{ℓ_{1} : c′_{1}, …, ℓ_{n} : c′_{n}\} \end{matrix} (m ≥ n)

(RecordSub)

Γ ⊢_{K} c_{i} ⪯ c′_{i} for 1 ≤ i ≤ m Γ ⊢_{K} c′_{i} type for m < i ≤ n

Γ ⊢_{K} \begin{matrix}⟨ℓ_{1} : c_{1}, …, ℓ_{n} : c_{m}⟩ ⪯ \\ ⟨ℓ_{1} : c′_{1}, …, ℓ_{n} : c′_{n}⟩ \end{matrix} (m ≤ n)

(UnionSub)

Γ ⊢_{K} c_{1} ⪯ c_{2} Γ ⊢_{K} c_{1} : Type_{i} Γ ⊢_{K} c_{2} : Type_{i}
Γ ⊢_{K} c_{1} : P_{i}(c_{2})

(PowIntro)

{Γ ⊢_{K} c_{1} : P_{i}(c_{2}) \atop Γ ⊢_{K} c_{1} ⪯ c_{2}}  (PowElim)

Typing

Γ ⊢_{K} x : c (Γ(x) = c)  (Var)
\begin{matrix}Γ ⊢_{K} c_{1} type & Γ[x : c_{1}] ⊢_{K} e : c_{2} \\ Γ ⊢_{K} λx:c_{1}.e : c_{1} → c_{2} \end{matrix} (x ∉ Γ)

(ArrowIntro)

\begin{matrix}Γ ⊢_{K} e_{1} : c_{1} → c_{2} & Γ ⊢_{K} e_{2} : c_{1} \\ Γ ⊢_{K} e_{1}e_{2} : c_{2} \end{matrix} (ArrowElim)
\begin{matrix}Γ ⊢_{K} c_{1} type & Γ[x : c_{1}] ⊢_{K} e ↓ c_{2} \\ Γ ⊢_{K} λx:c_{1}.e : c_{1} ⇒ c_{2} \end{matrix} (x ∉ Γ)

(TArrowIntro)

\begin{matrix}Γ ⊢_{K} e_{1} : c_{1} ⇒ c_{2} & Γ ⊢_{K} e_{2} : c_{1} \\ Γ ⊢_{K} e_{1}e_{2} : c_{2} \end{matrix} (TArrowElim)
Γ ⊢_{K} κ kind Γ[α : κ] ⊢_{K} e ↓ c

Γ ⊢_{K} Λα:κ.e : ∀α:κ.c (α ∉ Γ)

(QuantIntro)

\begin{matrix}Γ ⊢_{K} e : ∀α:κ.c_{2} & Γ ⊢_{K} c_{1} : κ \\ Γ ⊢_{K} e[c_{1}] : c_{2}[c_{1}/α] \end{matrix} (QuantElim)

{Γ ⊢_{K} e_{i} : c_{i} for 1 ≤ i ≤ n \atop Γ ⊢_{K} \{ℓ_{1} = e_{1}, …, ℓ_{n} = e_{n}\} : \{ℓ_{1} : c_{1}, …, ℓ_{n} : c_{n}\}}

(RecordIntro)

{Γ ⊢_{K} e : \{ℓ_{1} : c_{1}, …, ℓ_{n} : c_{n}\} \atop Γ ⊢_{K} π_{ℓ_{i}}(e) : c_{i}} (1 ≤ i ≤ n)

(RecordElim)

{Γ ⊢_{K} e : τ \atop Γ ⊢_{K} inj_{ℓ}(e) : ⟨ℓ : τ⟩}  (UnionIntro)

\begin{matrix}Γ ⊢_{K} e : ⟨ℓ_{1} : τ_{1}, …, ℓ_{n} : τ_{n}⟩ \\ Γ[x_{i} : τ_{i}] ⊢_{K} e_{i} : τ (for 1 ≤ i ≤ n) \end{matrix}

Γ ⊢_{K} case(e, ℓ_{1} ▷ x_{1}.e_{1}, …, ℓ_{n} ▷ x_{n}.e_{n}) : τ (UnionElim)

{Γ ⊢_{K} e : (c_{1} → c_{2}) → (c_{1} → c_{2}) \atop Γ ⊢_{K} fix_{c_{1}→c_{2}}(e) : c_{1} → c_{2}}  (Fix)
{Γ ⊢_{K} m : ⟨⟨c⟩⟩ \atop Γ ⊢_{K} ext(m) : c}  (TermModElim)
\begin{matrix}Γ ⊢_{K} e : c_{1} & Γ ⊢_{K} c_{1} ⪯ c_{2} \\ Γ ⊢_{K} e : c_{2} \end{matrix}  (Subtype)

Valuability

Γ ⊢_{K} x ↓ c (Γ(x) = c)  (VarHalt)
\begin{matrix}Γ ⊢_{K} c_{1} type & Γ[x : c_{1}] ⊢_{K} e : c_{2} \\ Γ ⊢_{K} λx:c_{1}.e ↓ c_{1} → c_{2} \end{matrix} (x ∉ Γ)

(ArrowIntroHalt)

\begin{matrix}Γ ⊢_{K} c_{1} type & Γ[x : c_{1}] ⊢_{K} e ↓ c_{2} \\ Γ ⊢_{K} λx:c_{1}.e ↓ c_{1} ⇒ c_{2} \end{matrix} (x ∉ Γ)

(TArrowIntroHalt)

\begin{matrix}Γ ⊢_{K} e_{1} ↓ c_{1} ⇒ c_{2} & Γ ⊢_{K} e_{2} ↓ c_{1} \\ Γ ⊢_{K} e_{1}e_{2} ↓ c_{2} \end{matrix}

(TArrowElimHalt)

Γ ⊢_{K} κ kind Γ[α : κ] ⊢_{K} e ↓ c

Γ ⊢_{K} Λα:κ.e ↓ ∀α:κ.c (α ∉ Γ)

(QuantIntroHalt)

\begin{matrix}Γ ⊢_{K} e ↓ ∀α:κ.c_{2} & Γ ⊢_{K} c_{1} : κ \\ Γ ⊢_{K} e[c_{1}] ↓ c_{2}[c_{1}/α] \end{matrix} (QuantElimHalt)

{Γ ⊢_{K} e_{i} ↓ c_{i} for 1 ≤ i ≤ n \atop Γ ⊢_{K} \{ℓ_{1} = e_{1}, …, ℓ_{n} = e_{n}\} ↓ \{ℓ_{1} : c_{1}, …, ℓ_{n} : c_{n}\}}

(RecordIntroHalt)

{Γ ⊢_{K} e ↓ \{ℓ_{1} : c_{1}, …, ℓ_{n} : c_{n}\} \atop Γ ⊢_{K} π_{ℓ_{i}}(e) ↓ c_{i}} (i ≤ i ≤ n)

(RecordElimHalt)

{Γ ⊢_{K} e ↓ τ \atop Γ ⊢_{K} inj_{ℓ}(e) ↓ ⟨ℓ : τ⟩}  (UnionIntroHalt)

\begin{matrix}Γ ⊢_{K} e ↓ ⟨ℓ_{1} : τ_{1}, …, ℓ_{n} : τ_{n}⟩ \\ Γ[x_{i} : τ_{i}] ⊢_{K} e_{i} ↓ τ (for 1 ≤ i ≤ n) \end{matrix}

Γ ⊢_{K} case(e, ℓ_{1} ▷ x_{1}.e_{1}, …, ℓ_{n} ▷ x_{n}.e_{n}) ↓ τ (UnionElimHalt)

{Γ ⊢_{K} e ↓ (c_{1} → c_{2}) ⇒ (c_{1} → c_{2}) \atop Γ ⊢_{K} fix_{c_{1}→c_{2}}(e) ↓ c_{1} → c_{2}}  (FixHalt)
{Γ ⊢_{K} m ↓ ⟨⟨c⟩⟩ \atop Γ ⊢_{K} ext(m) ↓ c}  (TermModElimHalt)
\begin{matrix}Γ ⊢_{K} e ↓ c_{1} & Γ ⊢_{K} c_{1} ⪯ c_{2} \\ Γ ⊢_{K} e ↓ c_{2} \end{matrix}  (SubtypeHalt)

Signature Formation and Subsignatures

\begin{matrix}Γ ⊢_{K} σ_{1} ⪯ σ_{2} & Γ ⊢_{K} σ_{2} ⪯ σ_{3} \\ Γ ⊢_{K} σ_{1} ⪯ σ_{3} \end{matrix} (SubsigTrans)
{Γ ⊢_{K} κ_{1} ⪯ κ_{2} \atop Γ ⊢_{K} ⟨κ_{1}⟩ ⪯ ⟨κ_{2}⟩}  (TypeSig)
{Γ ⊢_{K} c_{1} ⪯ c_{2} \atop Γ ⊢_{K} ⟨⟨c_{1}⟩⟩ ⪯ ⟨⟨c_{2}⟩⟩}  (TermSig)

Γ ⊢_{K} σ′_{1} ⪯ σ_{1} Γ[s : σ′_{1}] ⊢_{K} σ_{2} ⪯ σ′_{2} Γ[s : σ_{1}] ⊢_{K} σ_{2} sig

Γ ⊢_{K} Πs:σ_{1}.σ_{2} ⪯ Πs:σ′_{1}.σ′_{2} (PiSig)

Γ[s_{j} : σ_{j}]^{[j=1...i−1]} ⊢_{K} σ_{i} ⪯ σ′_{i} for 1 ≤ i ≤ n Γ[s_{j} : σ_{j}]^{[j=1...i−1]} ⊢_{K} σ_{i} sig for n < i ≤ m Γ[s_{j} : σ′_{j}]^{[j=1...i−1]} ⊢_{K} σ′_{i} sig for 1 ≤ i ≤ n

Γ ⊢_{K} {ℓ_{i} ▷ s_{i} : σ_{i}^{[i=1...m]}} ⪯ {ℓ_{i} ▷ s_{i} : σ′_{i}^{[i=1...n]}}

(s_{i}^{[i=1...m]} ∉ Γ, m ≥ n)

(RecordSig)

Module Formation

Γ ⊢_{K} s : σ (Γ(s) = σ)  (ModVar)
{Γ ⊢_{K} c : κ \atop Γ ⊢_{K} ⟨c⟩ : ⟨κ⟩}  (TypeMod)
{Γ ⊢_{K} e : c \atop Γ ⊢_{K} ⟨⟨e⟩⟩ : ⟨⟨c⟩⟩}  (TermMod)
\begin{matrix}Γ ⊢_{K} σ sig & Γ[s : σ_{1}] ⊢_{K} m : σ_{2} \\ Γ ⊢_{K} λs:σ_{1}.m : Πs:σ_{1}.σ_{2} \end{matrix} (PiModIntro)
\begin{matrix}Γ ⊢_{K} m_{1} : Πs:σ_{1}.σ_{2} & Γ ⊢_{K} m_{2} : σ_{1} \\ Γ ⊢_{K} m_{1}m_{2} : σ_{2}[m_{2}/s] \end{matrix} (PiModElim)

\begin{matrix}Γ ⊢_{K} m_{i} : σ_{i}[m_{j}^{[j=1...i−1]}/s_{j}^{[j=1...i−1]}] \\ (for 1 ≤ i ≤ n) \end{matrix}

Γ ⊢_{K} {ℓ_{i} ▷ s_{i} : σ_{i}^{[i=1...n]}} sig Γ ⊢_{K} {ℓ_{i} = m_{i}^{[i=1...n]}} : {ℓ_{i} ▷ s_{i} : σ_{i}^{[i=1...n]}}

(RecordModIntro)

{Γ ⊢_{K} m : \{ℓ_{j} ▷ s_{j} : σ_{j}^{[i=1...n]}\} \atop Γ ⊢_{K} π_{ℓ_{i}}(m) : σ_{i}[π_{ℓ_{j}}(m)^{[j=1...i−1]}/s_{j}^{[j=1...i−1]}]} ; (1 ≤ i ≤ n)

(RecordModElim)

{Γ ⊢_{K} m : σ \atop Γ ⊢_{K} (m : σ) : σ}  (Coerce)
{Γ ⊢_{K} λs:σ_{1}.ms : Πs:σ_{1}.σ_{2} \atop Γ ⊢_{K} m : Πs:σ_{1}.σ_{2}} (s ∉ m) (PiModEta)

{Γ ⊢_{K} \{ℓ_{i} = π_{ℓ_{i}}(m)^{[i=1...n]}\} : \{ℓ_{i} ▷ s_{i} : σ_{i}^{[i=1...n]}\} \atop Γ ⊢_{K} m : \{ℓ_{i} ▷ s_{i} : σ_{i}^{[i=1...n]}\}}

(RecordModEta)

Module Valuability

Γ ⊢_{K} s ↓ σ (Γ(s) = σ)  (ModVarHalt)
{Γ ⊢_{K} c : κ \atop Γ ⊢_{K} ⟨c⟩ ↓ ⟨κ⟩}  (TypeModHalt)
{Γ ⊢_{K} e ↓ c \atop Γ ⊢_{K} ⟨⟨e⟩⟩ ↓ ⟨⟨c⟩⟩}  (TermModHalt)

\begin{matrix}Γ ⊢_{K} σ sig & Γ[s : σ_{1}] ⊢_{K} m : σ_{2} \\ Γ ⊢_{K} λs:σ_{1}.m ↓ Πs:σ_{1}.σ_{2} \end{matrix}

(PiModIntroHalt)

\begin{matrix}Γ ⊢_{K} m_{i} ↓ σ_{i}[m_{j}^{[j=1...i−1]}/s_{j}^{[j=1...i−1]}] \\ (for 1 ≤ i ≤ n) \end{matrix}

Γ ⊢_{K} {ℓ_{i} ▷ s_{i} : σ_{i}^{[i=1...n]}} sig Γ ⊢_{K} {ℓ_{i} = m_{i}^{[i=1...n]}} ↓ {ℓ_{i} ▷ s_{i} : σ_{i}^{[i=1...n]}}

(RecordModIntroHalt)

{Γ ⊢_{K} m ↓ \{ℓ_{j} ▷ s_{j} : σ_{j}^{[i=1...n]}\} \atop Γ ⊢_{K} π_{ℓ_{i}}(m) ↓ σ_{i}[π_{ℓ_{j}}(m)^{[j=1...i−1]}/s_{j}^{[j=1...i−1]}]} ; (1 ≤ i ≤ n)

(RecordModElimHalt)

{Γ ⊢_{K} m ↓ σ \atop Γ ⊢_{K} (m : σ) ↓ σ}  (CoerceHalt)

Appendix B

Nuprl Proof Rules

Conventions

• Variables bound in a sequent are taken to be distinct. For example, Rule 2 has the implicit side condition that x not be bound by H, and Rule 3 has the implicit side-condition that x and y are distinct variables and neither is bound by H or J.

• Rules apply only when the consequent and subgoals are closed sequents. For example, Rule 2 has the implicit side-condition that x not appear free in H, and also (since, by the first convention, x cannot be bound by H) that x not appear free in J, C or t.

• When the inhabitant is omitted in the bottom (consequent) of a rule, the inhabitant is taken to be ⋆.

• Rules that may be derived from other rules are marked with a ‘D’.

Structural Rules

H; x : T; J ⊢_{ν} x in T  (1)

{H; J ⊢_{ν} C ◁ t \atop H; x : T; J ⊢_{ν} C ◁ t}  (2)

{H; y : T; x : S; J ⊢_{ν} C ◁ t \atop H; x : S; y : T; J ⊢_{ν} C ◁ t}  (3)

{H ⊢_{ν} t in C \atop H ⊢_{ν} C ◁ t}  (4)

{H ⊢_{ν} C ◁ t \atop H ⊢_{ν} t in C}  (5)

\begin{matrix}H ⊢_{ν} T ◁ t′ & H; x : T ⊢_{ν} C ◁ t \\ H ⊢_{ν} C[t′/x] ◁ t[t′/x] \end{matrix}  (D6)

Substitution

\begin{matrix}H ⊢_{ν} C[t′/x] ◁ s \\ H ⊢_{ν} t = t′ in T  H; x : T ⊢_{ν} C type \end{matrix} ; H ⊢_{ν} C[t/x] ◁ s (7)

Direct Computation

\begin{matrix}H ⊢_{ν} C[t′/x] ◁ s & ⊢_{ν} t ∼ t′ \\ H ⊢_{ν} C[t/x] ◁ s \end{matrix}  (8)

\begin{matrix}H; x : T [t′/y]; J ⊢_{ν} C ◁ s & ⊢_{ν} t ∼ t′ \\ H; x : T [t/y]; J ⊢_{ν} C ◁ s \end{matrix}  (9)

⊢_{ν} t ∼ t  (10)

{⊢_{ν} t′ ∼ t \atop ⊢_{ν} t ∼ t′}  (11)

\begin{matrix}⊢_{ν} t_{1} ∼ t_{2} & ⊢_{ν} t_{2} ∼ t_{3} \\ ⊢_{ν} t_{1} ∼ t_{3} \end{matrix}  (12)

\begin{matrix}⊢_{ν} t_{1} ∼ t′_{1} & ⊢_{ν} t_{2} ∼ t′_{2} \\ ⊢_{ν} t_{1}[t_{2}/x] ∼ t′_{1}[t′_{2}/x] \end{matrix}  (13)

⊢_{ν} t ∼ t′ (t ↦_{s} t′)  (14)

The evaluation step relation is defined in Figure 4.3.

x appears actively in t_{2}

⊢_{ν} t_{2}[t_{1}/x] ∼ let x = t_{1} in t_{2} (15)

The active positions of a term are defined in Definition 4.5.

x_{1} ∉ t_{2} and x_{2} ∉ t_{1}
⊢_{ν} let x_{1} = t_{1} in let x_{2} = t_{2} in t ∼ let x_{2} = t_{2} in let x_{1} = t_{1} in t

(16)

Equality

{H ⊢_{ν} t_{2} = t_{1} in T \atop H ⊢_{ν} t_{1} = t_{2} in T}  (17)

\begin{matrix}H ⊢_{ν} t_{1} = t_{2} in T & H ⊢_{ν} t_{2} = t_{3} in T \\ H ⊢_{ν} t_{1} = t_{3} in T \end{matrix}  (18)

{H ⊢_{ν} t = t′ in T \atop H ⊢_{ν} T type}  (19)

Subtyping

H; x : T ⊢_{ν} x in T′ H ⊢_{ν} T type H ⊢_{ν} T′ type

H ⊢_{ν} T ⪯ T′ (20)

\begin{matrix}H ⊢_{ν} t_{1} = t_{2} in T′ & H ⊢_{ν} T′ ⪯ T \\ H ⊢_{ν} t_{1} = t_{2} in T \end{matrix}  (21)

\begin{matrix}H; x : T′; J ⊢_{ν} C ◁ s & H ⊢_{ν} T ⪯ T′ \\ H; x : T; J ⊢_{ν} C ◁ s \end{matrix}  (22)

{H ⊢_{ν} T ⪯ T′ \atop H ⊢_{ν} T type}  (23)

{H ⊢_{ν} T ⪯ T′ \atop H ⊢_{ν} T′ type}  (24)

Universes

H ⊢_{ν} 𝕌_{i} in 𝕌_{j} (i < j)  (25)

{H ⊢_{ν} T in 𝕌_{i} \atop H ⊢_{ν} T in 𝕌_{j}} (i < j)  (26)

{H ⊢_{ν} T = T′ in 𝕌_{i} \atop H ⊢_{ν} T ⪯ T′}  (27)

Void

H ⊢_{ν} Void in 𝕌_{i}  (28)

{H ⊢_{ν} t in Void \atop H ⊢_{ν} C}  (29)

Atom

H ⊢_{ν} Atom in 𝕌_{i}  (30)

H ⊢_{ν} a in Atom (string literal a) (31)

H ⊢_{ν} a_{1} = a′_{1} in Atom H ⊢_{ν} a_{2} = a′_{2} in Atom
H ⊢_{ν} (a_{1} =_{A} a_{2}) = (a′_{1} =_{A} a′_{2}) in 𝔹

(32)

H ⊢_{ν} a_{1} = a_{2} in Atom

H ⊢_{ν} (a_{1} =_{A} a_{2}) = true in 𝔹 (33)

H ⊢_{ν} (a_{1} =_{A} a_{2}) = true in 𝔹

H ⊢_{ν} a_{1} = a_{2} in Atom (34)

Integer

H ⊢_{ν} ℤ in 𝕌_{i}  (35)

H ⊢_{ν} n in ℤ (integer literal n)  (36)

H ⊢_{ν} C[0/x] ◁ b H; x : ℤ; y : (0 < x); z : C[x − 1/x] ⊢_{ν} C ◁ u H; x : ℤ; y : (x < 0); z : C[x + 1/x] ⊢_{ν} C ◁ d

H; x : ℤ ⊢_{ν} C ◁

fix(λf.λx. if x =_{Z} 0 then b else if 0 ≤_{Z} x then u[⋆, f(x − 1)/y, z]

else d[⋆, f(x + 1)/y, z]) x

(37)

\begin{matrix}H ⊢_{ν} n_{1} = n′_{1} in ℤ & H ⊢_{ν} n_{2} = n′_{2} in ℤ \\ H ⊢_{ν} (n_{1} =_{Z} n_{2}) = (n′_{1} =_{Z} n′_{2}) in 𝔹 \end{matrix} (38)
H ⊢_{ν} n_{1} = n_{2} in ℤ

H ⊢_{ν} (n_{1} =_{Z} n_{2}) = true in 𝔹 (39)

H ⊢_{ν} (n_{1} =_{Z} n_{2}) = true in 𝔹

H ⊢_{ν} n_{1} = n_{2} in ℤ (40)

\begin{matrix}H ⊢_{ν} n_{1} = n′_{1} in ℤ & H ⊢_{ν} n_{2} = n′_{2} in ℤ \\ H ⊢_{ν} (n_{1} ≤_{Z} n_{2}) = (n′_{1} ≤_{Z} n′_{2}) in 𝔹 \end{matrix} (41)

{H ⊢_{ν} n_{1} ≤ n_{2} \atop H ⊢_{ν} (n_{1} ≤_{Z} n_{2}) = true in 𝔹}  (42)

{H ⊢_{ν} (n_{1} ≤_{Z} n_{2}) = true in 𝔹 \atop H ⊢_{ν} n_{1} ≤ n_{2}}  (43)

\begin{matrix}H ⊢_{ν} n_{1} = n′_{1} in ℤ & H ⊢_{ν} n_{2} = n′_{2} in ℤ \\ H ⊢_{ν} n_{1} ⊕ n_{2} = n′_{1} ⊕ n′_{2} in ℤ \end{matrix} (44)

Not shown are the various rules governing the behavior of the binary operations ⊕.

Disjoint Union

\begin{matrix}H ⊢_{ν} A = A′ in 𝕌_{i} & H ⊢_{ν} B = B′ in 𝕌_{i} \\ H ⊢_{ν} A + B = A′ + B′ in 𝕌_{i} \end{matrix} (45)
H ⊢_{ν} A type H ⊢_{ν} B type

H ⊢_{ν} A + B type (46)

H ⊢_{ν} a = a′ in A H ⊢_{ν} A + B type

H ⊢_{ν} inj_{1}(a) = inj_{1}(a′) in A + B

(47)

H ⊢_{ν} b = b′ in B H ⊢_{ν} A + B type

H ⊢_{ν} inj_{2}(b) = inj_{2}(b′) in A + B

(48)

H ⊢_{ν} t = t′ in A + B H; x : A ⊢_{ν} t_{1} = t′_{1} in T H; x : B ⊢_{ν} t_{2} = t′_{2} in T

H ⊢_{ν} case(t, x.t_{1}, x.t_{2}) = case(t′, x.t′_{1}, x.t′_{2}) in T (49)

H; y : A; J[inj_{1}(y)/x]

⊢_{ν} C[inj_{1}(y)/x] ◁ s[inj_{1}(y)/x] H; y : B; J[inj_{2}(y)/x]

⊢_{ν} C[inj_{2}(y)/x] ◁ s[inj_{2}(y)/x]

H; x : A + B; J ⊢_{ν} C ◁ s (50)

Products

H ⊢_{ν} A = A′ in 𝕌_{i} H; x : A ⊢_{ν} B = B′ in 𝕌_{i}

H ⊢_{ν} Σx:A.B = Σx:A′.B′ in 𝕌_{i} (51)

H ⊢_{ν} A type H; x : A ⊢_{ν} B type

H ⊢_{ν} Σx:A.B type (52)

H ⊢_{ν} a = a′ in A

H ⊢_{ν} b = b′ in B[a/x] H ⊢_{ν} Σx:A.B type

H ⊢_{ν} ⟨a, b⟩ = ⟨a′, b′⟩ in Σx:A.B (53)

{H ⊢_{ν} t = t′ in Σx:A.B \atop H ⊢_{ν} π_{1}(t) = π_{1}(t′) in A}  (54)

{H ⊢_{ν} t = t′ in Σx:A.B \atop H ⊢_{ν} π_{2}(t) = π_{2}(t′) in B[π_{1}(t)/x]} (55)

\begin{matrix}H; x : A; y : B; J[⟨x, y⟩/z] \\ ⊢_{ν} C[⟨x, y⟩/z] ◁ s[⟨x, y⟩/z] \end{matrix} ; H; z : Σx:A.B; J ⊢_{ν} C ◁ s  (56)

H ⊢_{ν} π_{1}(p_{1}) = π_{1}(p_{2}) in A
H ⊢_{ν} π_{2}(p_{1}) = π_{2}(p_{2}) in B[π_{1}(p_{1})/x]
H ⊢_{ν} p_{1} in Σx:A_{1}.B_{1}
H ⊢_{ν} p_{2} in Σx:A_{2}.B_{2}
H ⊢_{ν} Σx:A.B type

H ⊢_{ν} p_{1} = p_{2} in Σx:A.B (57)

Functions

H ⊢_{ν} A = A′ in 𝕌_{i} H; x : A ⊢_{ν} B = B′ in 𝕌_{i}
H ⊢_{ν} Πx:A.B = Πx:A′.B′ in 𝕌_{i}

(58)

H ⊢_{ν} A type H; x : A ⊢_{ν} B type

H ⊢_{ν} Πx:A.B type (59)

H ⊢_{ν} A type H; x : A ⊢_{ν} b = b′ in B

H ⊢_{ν} λx.b = λx.b′ in Πx:A.B (60)

\begin{matrix}H ⊢_{ν} f = f′ in Πx:A.B & H ⊢_{ν} a = a′ in A \\ H ⊢_{ν} f a = f′ a′ in B[a/x] \end{matrix} (61)

H; x : A ⊢_{ν} f_{1} x = f_{2} x in B H ⊢_{ν} A type H ⊢_{ν} f_{1} in Πx:A_{1}.B_{1} H ⊢_{ν} f_{2} in Πx:A_{2}.B_{2}

H ⊢_{ν} f_{1} = f_{2} in Πx:A.B (62)

Very-Dependent Functions

H ⊢_{ν} A = A′ in 𝕌_{i}

H; y : A; z : A ⊢_{ν} P in 𝕌_{i}

H; x : A ⊢_{ν} 0 ≤ e

H; y : A; z : A; w : P ⊢_{ν} e[y/x] < e[z/x]

H; \begin{matrix}x : A; \\ f : \{g | y : \{y : A | P [x/z]\} → B[g, y/f, x]\} \\ ⊢_{ν} B = B′ in 𝕌_{i} \end{matrix}

H ⊢_{ν} {f | x:A → B} = {f | x:A′ → B′} in 𝕌_{i} (63)

H ⊢_{ν} A type H; y : A; z : A ⊢_{ν} P type H; x : A ⊢_{ν} 0 ≤ e H; y : A; z : A; w : P ⊢_{ν} e[y/x] < e[z/x]

H; \begin{matrix}x : A; \\ f : \{g | y : \{y : A | P [x/z]\} → B[g, y/f, x]\} \\ ⊢_{ν} B type \end{matrix}

H ⊢_{ν} {f | x:A → B} type

(64)

\begin{matrix}H ⊢_{ν} \{f | x:A → b\} type \\ H; x : A ⊢_{ν} b = b′ in B[λx.b/f] \end{matrix}

H ⊢_{ν} λx.b = λx.b′ in {f | x:A → b} (65)

\begin{matrix}H ⊢_{ν} g = g′ in \{f | x:A → B\} & H ⊢_{ν} a = a′ in A \\ H ⊢_{ν} g a = g′ a′ in B[g, a/f, x] \end{matrix}

(66)

H; x : A ⊢_{ν} g_{1}x = g_{2}x in B[g_{1}/f] H ⊢_{ν} {f | x:A → B} type H ⊢_{ν} g_{1} in {f | x:A_{1} → B_{1}} H ⊢_{ν} g_{2} in {f | x:A_{2} → B_{2}}

H ⊢_{ν} g_{1} = g_{2} in {f | x:A → B} (67)

Partial Types

H ⊢_{ν} T = T′ in 𝕌_{i} H ⊢_{ν} T total

H ⊢_{ν} \overline{T} = \overline{T′} in 𝕌_{i} (68)

H ⊢_{ν} T total ; H ⊢_{ν} \overline{T} type  (69)
H ⊢_{ν} t = t′ in T H ⊢_{ν} \overline{T} type

H ⊢_{ν} t = t′ in \overline{T} (70)

H; x : (t_{1} in! T_{1}) ⊢_{ν} t_{1} = t_{2} in T

H ⊢_{ν} t_{1} in! T_{1} ⇔ t_{2} in! T_{2} H ⊢_{ν} \overline{T} type

H ⊢_{ν} t_{1} = t_{2} in \overline{T}

(71)

H ⊢_{ν} f = f′ in \overline{T} → \overline{T} H ⊢_{ν} T admiss
H ⊢_{ν} fix(f) = fix(f′) in \overline{T}

(72)

\begin{matrix}H ⊢_{ν} T_{1} ⪯ T_{2} & H ⊢_{ν} T_{2} total \\ H ⊢_{ν} \overline{T_{1}} ⪯ \overline{T_{2}} \end{matrix}  (D73)

Convergence

\begin{matrix}H ⊢_{ν} t in! T′ & H ⊢_{ν} t in \overline{T} \\ H ⊢_{ν} t in! T \end{matrix}  (74)
{H ⊢_{ν} t in \overline{T} \atop H ⊢_{ν} t in! T} (t canonical)  (75)
{H ⊢_{ν} t in! T \atop H ⊢_{ν} t in \overline{T}}  (76)
\begin{matrix}H ⊢_{ν} t in! T′ & H ⊢_{ν} t = t′ in \overline{T} \\ H ⊢_{ν} t = t′ in T \end{matrix}  (77)
H; x : T ⊢_{ν} x in! T′ H ⊢_{ν} T type

H ⊢_{ν} T total (78)

H ⊢_{ν} t in T H ⊢_{ν} T total

H ⊢_{ν} t in! T (79)

H ⊢_{ν} let x = t_{1} in t_{2} in! B H ⊢_{ν} t_{1} in \overline{A}

H ⊢_{ν} t_{1} in! A (80)

Sequencing

H ⊢_{ν} t_{1} = t′_{1} in \overline{B}
H; x : B ⊢_{ν} t_{2} = t′_{2} in \overline{A} H ⊢_{ν} \overline{A} type

H ⊢_{ν} (let x = t_{1} in t_{2}) = (let x = t′_{1} in t′_{2}) in \overline{A}

(81)

H ⊢_{ν} t_{1} in! B H; x : B ⊢_{ν} t_{2} in A

H ⊢_{ν} (let x = t_{1} in t_{2}) = t_{2}[t_{1}/x] in A (82)

Set

H ⊢_{ν} A = A′ in 𝕌_{i}
H; x : A ⊢_{ν} B in 𝕌_{i}
H; x : A ⊢_{ν} B′ in 𝕌_{i}
H; x : A ⊢_{ν} B ⇔ B′

H ⊢_{ν} {x : A | B} = {x : A′ | B′} in 𝕌_{i} (83)

H ⊢_{ν} A type H; x : A ⊢_{ν} B type

H ⊢_{ν} {x : A | B} type (84)

\begin{matrix}H ⊢_{ν} a = a′ in A \\ H ⊢_{ν} B[a/x] H ⊢_{ν} \{x : A | B\} type \end{matrix}

H ⊢_{ν} a = a′ in {x : A | B} (85)

\begin{matrix}H; x : A ⊢_{ν} B type & H; x : A; y : B; J ⊢_{ν} C ◁ s \\ H; x : \{x : A | B\}; J ⊢_{ν} C ◁ s \end{matrix}

(86)

Note that in Rule 86, y may not appear free in J, C or s, by convention.

Quotient

H ⊢_{ν} T = T′ in 𝕌_{i} H; x : T; y : T ⊢_{ν} E in 𝕌_{i} H; x : T; y : T ⊢_{ν} E′ in 𝕌_{i} H; x : T; y : T ⊢_{ν} E ⇔ E′ H; x : T ⊢_{ν} E[x/y] H; x : T; y : T; z : E ⊢_{ν} E[y, x/x, y] H; x : T; y : T; z : T;

w : E; w′ : E[y, z/x, y] ⊢_{ν} E[z/y]

H ⊢_{ν} xy:T//E = xy:T′//E′ in 𝕌_{i} (87)

H ⊢_{ν} T type H; x : T; y : T ⊢_{ν} E type H; x : T ⊢_{ν} E[x/y] H; x : T; y : T; z : E ⊢_{ν} E[y, x/x, y] H; x : T; y : T; z : T;

w : E; w′ : E[y, z/x, y] ⊢_{ν} E[z/y]

H ⊢_{ν} xy:T//E type (88)

H ⊢_{ν} t = t′ in T H ⊢_{ν} xy:T//E type

H ⊢_{ν} t = t′ in xy:T//E (89)

H ⊢_{ν} t in T H ⊢_{ν} t′ in T H ⊢_{ν} e in E[t, t′/x, y] H ⊢_{ν} xy:T//E type

H ⊢_{ν} t = t′ in xy:T//E (90)

H; x : A; y : A ⊢_{ν} E type H; x : (xy:T//E); J ⊢_{ν} T type H; x : A; y : A; z : B; J ⊢_{ν} s = s′[y/x] in T

H; x : (xy:T//E); J ⊢_{ν} s = s′ in T (91)

Every Type

H ⊢_{ν} 𝔼 ∈ 𝕌_{i}  (92)

\begin{matrix}H ⊢_{ν} t in! T & H ⊢_{ν} t′ in! T′ \\ H ⊢_{ν} t = t′ in 𝔼 \end{matrix}  (93)

H ⊢_{ν} t in T H ⊢_{ν} t′ in T′

H ⊢_{ν} t = t′ in Top (94)

{H ⊢_{ν} T total \atop H ⊢_{ν} \overline{T} ⪯ \overline{𝔼}}  (D95)

Equality Type

\begin{matrix}H ⊢_{ν} T = T′ in 𝕌_{i} \\ H ⊢_{ν} t_{1} = t′_{1} in T H ⊢_{ν} t_{2} = t′_{2} in T \end{matrix}

H ⊢_{ν} (t_{1} = t_{2} in T) = (t′_{1} = t′_{2} in T′) in 𝕌_{i} (96)

H ⊢_{ν} T type H ⊢_{ν} t_{1} in T H ⊢_{ν} t_{2} in T

H ⊢_{ν} (t_{1} = t_{2} in T) type (97)

{H ⊢_{ν} t = t′ in T \atop H ⊢_{ν} ⋆ in (t = t′ in T)}  (98)

{H; x : (t = t′ in T); J[⋆/x] ⊢_{ν} C[⋆/x] ◁ s[⋆/x] \atop H; x : (t = t′ in T); J ⊢_{ν} C ◁ s}

(99)

Subtyping Type

\begin{matrix}H ⊢_{ν} T_{1} = T′_{1} in 𝕌_{i} & H ⊢_{ν} T_{2} = T′_{2} in 𝕌_{i} \\ H ⊢_{ν} (T_{1} ⪯ T_{2}) = (T′_{1} ⪯ T′_{2}) in 𝕌_{i} \end{matrix} (100)
H ⊢_{ν} T_{1} type H ⊢_{ν} T_{2} type

H ⊢_{ν} (T_{1} ⪯ T_{2}) type (101)

{H ⊢_{ν} T ⪯ T′ \atop H ⊢_{ν} ⋆ in (T ⪯ T′)}  (102)

{H; x : (T ⪯ T′); J[⋆/x] ⊢_{ν} C[⋆/x] ◁ s[⋆/x] \atop H; x : (T ⪯ T′); J ⊢_{ν} C ◁ s} (103)

Inequality Type

\begin{matrix}H ⊢_{ν} n_{1} = n′_{1} in ℤ & H ⊢_{ν} n_{2} = n′_{2} in ℤ \\ H ⊢_{ν} (n_{1} ≤ n_{2}) = (n′_{1} ≤ n′_{2}) in 𝕌_{i} \end{matrix} (104)

{H ⊢_{ν} n ≤ n′ \atop H ⊢_{ν} ⋆ in (n ≤ n′)}  (105)

{H; x : (n ≤ n′); J[⋆/x] ⊢_{ν} C[⋆/x] ◁ s[⋆/x] \atop H; x : (n ≤ n′); J ⊢_{ν} C ◁ s} (106)

Convergence Type

H ⊢_{ν} \overline{T} = \overline{T′} in 𝕌_{i} H ⊢_{ν} t = t′ in \overline{T}

H ⊢_{ν} (t in! T) = (t′ in! T′) in 𝕌_{i} (107)

H ⊢_{ν} t in \overline{T}

H ⊢_{ν} (t in! T) type (108)

{H ⊢_{ν} t in! T \atop H ⊢_{ν} ⋆ in (t in! T)}  (109)
{H; x : (t in! T); J[⋆/x] ⊢_{ν} C[⋆/x] ◁ s[⋆/x] \atop H; x : (t in! T); J ⊢_{ν} C ◁ s} (110)

Totality Type

H ⊢_{ν} T = T′ in 𝕌_{i} H ⊢_{ν} (T total) = (T′ total) in 𝕌_{i}

(111)

H ⊢_{ν} T type H ⊢_{ν} (T total) type

(112)

H ⊢_{ν} T total

H ⊢_{ν} ⋆ in (T total) (113)

H; x : (T total); J[⋆/x] ⊢_{ν} C[⋆/x] ◁ s[⋆/x]

H; x : (T total); J ⊢_{ν} C ◁ s (114)

Admissibility Type

H ⊢_{ν} T = T′ in 𝕌_{i}

H ⊢_{ν} (T admiss) = (T′ admiss) in 𝕌_{i}

(115)

H ⊢_{ν} T type H ⊢_{ν} (T admiss) type

(116)

H ⊢_{ν} T admiss H ⊢_{ν} ⋆ in (T admiss)

(117)

H; x : (T admiss); J[⋆/x] ⊢_{ν} C[⋆/x] ◁ s[⋆/x]

H; x : (T admiss); J ⊢_{ν} C ◁ s (118)

Totality

H ⊢_{ν} T total ; H ⊢_{ν} T type  (119)
H ⊢_{ν} 𝕌_{i} total  (120)
H ⊢_{ν} Void total  (121)
H ⊢_{ν} Atom total  (122)
H ⊢_{ν} ℤ total  (123)

H ⊢_{ν} (A + B) type

H ⊢_{ν} (A + B) total (124)

H ⊢_{ν} (Σx:A.B) type

H ⊢_{ν} (Σx:A.B) total (125)

H ⊢_{ν} (Πx:A.B) type

H ⊢_{ν} (Πx:A.B) total (126)

H ⊢_{ν} A total H ⊢_{ν} {x : A | B} type

H ⊢_{ν} {x : A | B} total (127)

H ⊢_{ν} A total H ⊢_{ν} (xy:A//B) type

H ⊢_{ν} (xy:A//B) total (128)

H ⊢_{ν} 𝔼 total  (129)

H ⊢_{ν} (t = t′ in T) type

H ⊢_{ν} (t = t′ in T) total (130)

H ⊢_{ν} (T ⪯ T′) type

H ⊢_{ν} (T ⪯ T′) total (131)

H ⊢_{ν} (t in! T) type

H ⊢_{ν} (t in! T) total (132)

H ⊢_{ν} (T total) type H ⊢_{ν} (T total) total

(133)

H ⊢_{ν} (T admiss) type

H ⊢_{ν} (T admiss) total

(134)

Admissibility

H ⊢_{ν} T admiss

H ⊢_{ν} T type (135)

H ⊢_{ν} Void admiss  (136)
H ⊢_{ν} Atom admiss  (137)
H ⊢_{ν} ℤ admiss  (138)
H ⊢_{ν} A admiss H ⊢_{ν} B admiss

H ⊢_{ν} (A + B) admiss (139)

H ⊢_{ν} A admiss H ⊢_{ν} B admiss (x : A)

H ⊢_{ν} (Σx:A.B) admiss (140)

H; x : A ⊢_{ν} B admiss H ⊢_{ν} (Πx:A.B) type

H ⊢_{ν} (Πx:A.B) admiss

(141)

H ⊢_{ν} T admiss H ⊢_{ν} \overline{T} type

H ⊢_{ν} \overline{T} admiss (142)

H ⊢_{ν} A admiss H ⊢_{ν} B admiss (x : A) H; x : A; y : ↓B ⊢_{ν} B

H ⊢_{ν} {x : A | B} admiss (143)

H ⊢_{ν} A admiss H ⊢_{ν} B[π_{1}(z), π_{2}(z)/x, y] admiss (z : A × A) H; x : A; y : A; z : ↓B ⊢_{ν} B H ⊢_{ν} xy:A//B type

H ⊢_{ν} xy:A//B admiss

(144)

H ⊢_{ν} 𝔼 admiss  (145)

H ⊢_{ν} (t = t′ in T) type H ⊢_{ν} (t = t′ in T) admiss

(146)

H ⊢_{ν} (T ⪯ T′) type

H ⊢_{ν} (T ⪯ T′) admiss (147)

H ⊢_{ν} (t ≤ t′) type

H ⊢_{ν} (t ≤ t′) admiss (148)

H ⊢_{ν} (t in! T) type H ⊢_{ν} (t in! T) admiss

(149)

H ⊢_{ν} (T total) type H ⊢_{ν} (T total) admiss

(150)

H ⊢_{ν} (T admiss) type H ⊢_{ν} (T admiss) admiss

(151)

More Admissibility Type

\begin{matrix}H ⊢_{ν} A = A′ in 𝕌_{i} \\ H; x : A ⊢_{ν} B = B′ in 𝕌_{i} \end{matrix}

H ⊢_{ν} (B admiss (x : A)) =

(B′ admiss (x : A′)) in 𝕌_{i}

(152)

H ⊢_{ν} A type H; x : A ⊢_{ν} B type

H ⊢_{ν} (B admiss (x : A)) type

(153)

H ⊢_{ν} B admiss (x : A) H ⊢_{ν} ⋆ in (B admiss (x : A))

(154)

\begin{matrix}H; x : (B admiss (x : A)); J[⋆/x] \\ ⊢_{ν} C[⋆/x] ◁ s[⋆/x] \end{matrix}

H; x : (B admiss (x : A)); J ⊢_{ν} C ◁ s (155)

\begin{matrix}H ⊢_{ν} A = A′ in 𝕌_{i} \\ H; x : A ⊢_{ν} B = B′ in 𝕌_{i} \end{matrix}

H ⊢_{ν} (B wcoadmiss (x : A)) =

(B′ wcoadmiss (x : A′)) in 𝕌_{i}

(156)

H ⊢_{ν} A type H; x : A ⊢_{ν} B type

H ⊢_{ν} (B wcoadmiss (x : A)) type

(157)

H ⊢_{ν} B wcoadmiss (x : A) H ⊢_{ν} ⋆ in (B wcoadmiss (x : A))

(158)

H; x : (B wcoadmiss (x : A)); J[⋆/x]

⊢_{ν} C[⋆/x] ◁ s[⋆/x] H; x : (B wcoadmiss (x : A)); J ⊢_{ν} C ◁ s (159)

\begin{matrix}H ⊢_{ν} A = A′ in 𝕌_{i} \\ H; x : A ⊢_{ν} B = B′ in 𝕌_{i} \end{matrix}

H ⊢_{ν} (B coadmiss (x : A)) =

(B′ coadmiss (x : A′)) in 𝕌_{i}

(160)

H ⊢_{ν} A type H; x : A ⊢_{ν} B type

H ⊢_{ν} (B coadmiss (x : A)) type

(161)

H ⊢_{ν} B coadmiss (x : A) H ⊢_{ν} ⋆ in (B coadmiss (x : A))

(162)

H; x : (B coadmiss (x : A)); J[⋆/x]

⊢_{ν} C[⋆/x] ◁ s[⋆/x] H; x : (B coadmiss (x : A)); J ⊢_{ν} C ◁ s

(163)

H ⊢_{ν} T = T′ in 𝕌_{i} H ⊢_{ν} (T mono) = (T′ mono) in 𝕌_{i}

(164)

H ⊢_{ν} T type H ⊢_{ν} (T mono) type

(165)

H ⊢_{ν} T mono

H ⊢_{ν} ⋆ in (T mono) (166)

H; x : (T mono); J[⋆/x] ⊢_{ν} C[⋆/x] ◁ s[⋆/x]

H; x : (T mono); J ⊢_{ν} C ◁ s (167)

More Admissibility

H ⊢_{ν} A admiss (x : T) H ⊢_{ν} T H ⊢_{ν} A admiss (x ∉ A) (168)
H ⊢_{ν} A admiss H ⊢_{ν} T type H ⊢_{ν} A admiss (x : T) (x ∉ A) (169)
H ⊢_{ν} A type H ⊢_{ν} T type H ⊢_{ν} A wcoadmiss (x : T) (x ∉ A) (170)

H ⊢_{ν} A coadmiss (x : T) H ⊢_{ν} A wcoadmiss (x : T)

(171)

H ⊢_{ν} A mono

H ⊢_{ν} A admiss (172)

H ⊢_{ν} T type

H ⊢_{ν} Void coadmiss (x : T)

(173)

H ⊢_{ν} Void mono  (174)
H ⊢_{ν} T type
H ⊢_{ν} Atom coadmiss (x : T)

(175)

H ⊢_{ν} Atom mono  (176)
H ⊢_{ν} T type
H ⊢_{ν} ℤ coadmiss (x : T)

(177)

H ⊢_{ν} ℤ mono  (178)

H ⊢_{ν} A admiss (x : T) H ⊢_{ν} B admiss (x : T)

H ⊢_{ν} (A + B) admiss (x : T) (179)

H ⊢_{ν} A wcoadmiss (x : T) H ⊢_{ν} B wcoadmiss (x : T)

H ⊢_{ν} (A + B) wcoadmiss (x : T)

(180)

H ⊢_{ν} A coadmiss (x : T)
H ⊢_{ν} B coadmiss (x : T)
H ⊢_{ν} (A + B) coadmiss (x : T)

(181)

H ⊢_{ν} A mono H ⊢_{ν} B mono

H ⊢_{ν} (A + B) mono (182)

H ⊢_{ν} A admiss (y : T)

H ⊢_{ν} B[π_{1}(z), π_{2}(z)/y, x] admiss (z : Σy:T.A) H ⊢_{ν} (Σx:A.B) admiss (y : T)

(183)

H ⊢_{ν} A wcoadmiss (y : T)

H; y′ : T; x : A[y′/y] ⊢_{ν} B wcoadmiss (y : T)

H; y : T ⊢_{ν} Σx:A.B type
H ⊢_{ν} (Σx:A.B) wcoadmiss (y : T)

(184)

H ⊢_{ν} A coadmiss (y : T) H ⊢_{ν} B[π_{1}(z), π_{2}(z)/y, x] coadmiss (z : Σy:T.A)

H ⊢_{ν} (Σx:A.B) coadmiss (y : T)

(185)

H ⊢_{ν} A mono H; x : A ⊢_{ν} B mono

H ⊢_{ν} (Σx:A.B) mono (186)

H ⊢_{ν} A wcoadmiss (y : T) H; y′ : T; x : A[y′/y] ⊢_{ν} B admiss (y : T) H; y : T ⊢_{ν} Πx:A.B type

H ⊢_{ν} (Πx:A.B) admiss (y : T) (187)

H ⊢_{ν} A mono H; x : A ⊢_{ν} B mono

H ⊢_{ν} (Πx:A.B) mono (188)

H ⊢_{ν} A admiss (x : T) H; x : T ⊢_{ν} \overline{A} type

H ⊢_{ν} \overline{A} admiss (x : T)

(189)

H ⊢_{ν} A admiss y : T H ⊢_{ν} B[π_{1}(z), π_{2}(z)/y, x] admiss (z : Σy:T.A) H; y : T; x : A; z : ↓B ⊢_{ν} B H; y : T ⊢_{ν} {x : A | B} type

H ⊢_{ν} {x : A | B} admiss (y : T)

(190)

H ⊢_{ν} A wcoadmiss (y : T) H; y′ : T; x : A[y′/y] ⊢_{ν} B wcoadmiss (y : T) H; y : T ⊢_{ν} {x:A | B} type

H ⊢_{ν} {x : A | B} wcoadmiss (y : T)

(191)

H ⊢_{ν} A coadmiss (y : T) H ⊢_{ν} B[π_{1}(z), π_{2}(z)/y, x] coadmiss (z : Σy:T.A)

H ⊢_{ν} {x : A | B} coadmiss (y : T)

(192)

H ⊢_{ν} A mono H ⊢_{ν} {x : A | B} type

H ⊢_{ν} {x : A | B} mono (193)

H ⊢_{ν} A admiss z : T

\begin{matrix}H ⊢_{ν} B[π_{1}(w), π_{1}(π_{2}(w)), π_{2}(π_{2}(w))/z, x, y] \\ admiss(w : Σz:T.(A × A)) \end{matrix}

H; z : T; x : A; y : A; w : ↓B ⊢_{ν} B H; y : T ⊢_{ν} xy:A//B type

H ⊢_{ν} xy:A//B admiss (z : T)

(194)

H ⊢_{ν} A wcoadmiss (z : T) H; z′ : T; x : A[z′/z]; y : A[z′/z] ⊢_{ν}

B wcoadmiss (z : T) H; z : T ⊢_{ν} xy:A//B type H ⊢_{ν} xy:A//B wcoadmiss (z : T)

(195)

H ⊢_{ν} A coadmiss (z : T) H ⊢_{ν} B[π_{1}(w), π_{1}(π_{2}(w)), π_{2}(π_{2}(w))/z, x, y] coadmiss(w : Σy:T.(A × A))

H ⊢_{ν} xy:A//B coadmiss (z : T)

(196)

H ⊢_{ν} A mono H ⊢_{ν} xy:A//B type

H ⊢_{ν} xy:A//B mono (197)

H ⊢_{ν} T type H ⊢_{ν} 𝔼 coadmiss (x : T)

(198)

H ⊢_{ν} 𝔼 mono  (199)

H ⊢_{ν} A admiss (x : T) H; x : T ⊢_{ν} (a = a′ in A) type H ⊢_{ν} (a = a′ in A) admiss (x : T)

(200)

H ⊢_{ν} A coadmiss (x : T) H; x : T ⊢_{ν} (a = a′ in A) type

H ⊢_{ν} (a = a′ in A) coadmiss (x : T)

(201)

H ⊢_{ν} (a = a′ in A) type

H ⊢_{ν} (a = a′ in A) mono (202)

H; x : T ⊢_{ν} (a ≤ a′) type H ⊢_{ν} T type

H ⊢_{ν} (a ≤ a′) admiss (x : T) (203)

H; x : T ⊢_{ν} (a ≤ a′) type H ⊢_{ν} T type

H ⊢_{ν} (a ≤ a′) coadmiss (x : T)

(204)

H ⊢_{ν} (a ≤ a′) type

H ⊢_{ν} (a ≤ a′) in A) mono (205)

H; x : T ⊢_{ν} (a in! A) type H ⊢_{ν} T type

H ⊢_{ν} (a in! A) admiss (x : T)

(206)

H; x : T ⊢_{ν} (a in! A) type H ⊢_{ν} T type

H ⊢_{ν} (a in! A) coadmiss (x : T)

(207)

H ⊢_{ν} (a in! A) type

H ⊢_{ν} (a in! A) mono (208)

H; y : T ⊢_{ν} d in T_{1} + T_{2} H; y : T; x : T_{1} ⊢_{ν} A type H; y : T; x : T_{2} ⊢_{ν} B type

H ⊢_{ν} \begin{matrix}A[π_{1}(z), π_{2}(z)/y, x] \\ admiss(z : Σy:T.T_{1}) \end{matrix}

H ⊢_{ν} \begin{matrix}B[π_{1}(z), π_{2}(z)/y, x] \\ admiss(z : Σy:T.T_{2}) \end{matrix}

H ⊢_{ν} case(d, x.A, x.B) admiss (y : T)

(209)

H; y : T ⊢_{ν} d in T_{1} + T_{2} H; y : T; x : T_{1} ⊢_{ν} A type H; y : T; x : T_{2} ⊢_{ν} B type H ⊢_{ν} A[π_{1}(z), π_{2}(z)/y, x] wcoadmiss(z : Σy:T.T_{1}) H ⊢_{ν} B[π_{1}(z), π_{2}(z)/y, x] wcoadmiss(z : Σy:T.T_{2})

H ⊢_{ν} case(d, x.A, x.B) wcoadmiss (y : T)

(210)

H; y : T ⊢_{ν} d in T_{1} + T_{2} H; y : T; x : T_{1} ⊢_{ν} A type H; y : T; x : T_{2} ⊢_{ν} B type H ⊢_{ν} A[π_{1}(z), π_{2}(z)/y, x] coadmiss(z : Σy:T.T_{1}) H ⊢_{ν} B[π_{1}(z), π_{2}(z)/y, x] coadmiss(z : Σy:T.T_{2})

H ⊢_{ν} case(d, x.A, x.B) coadmiss (y : T)

(211)

Classical Rules

H ⊢ ∀P:ℙ_{i}. ↓(P ∨ ¬P) ◁ λx.⋆  (XM)
H ⊢_{ν} A wcoadmiss (x : T) H; x : T ⊢_{ν} \overline{A} type
H ⊢_{ν} \overline{A} wcoadmiss (x : T)

(PWC)

H ⊢_{ν} A coadmiss (x : T) H; x : T ⊢_{ν} \overline{A} type
H ⊢_{ν} \overline{A} coadmiss (x : T)

(PC)

H ⊢_{ν} A = A′ in 𝕌_{i}

H; y : A; z : A ⊢_{ν} P in 𝕌_{i}

H ⊢_{ν} ¬(∃f:ℕ → A. ∀n:ℕ. P [f(n + 1), f n/y, z])

H; \begin{matrix}x : A; \\ f : \{g | y : \{y : A | P [x/z]\} → B[g, y/f, x]\} \\ ⊢_{ν} B = B′ in 𝕌_{i} \end{matrix}

H ⊢_{ν} {f | x:A → B} = {f | x:A′ → B′} in 𝕌_{i}

(VF)

H ⊢_{ν} A type

H; y : A; z : A ⊢_{ν} P type

H ⊢_{ν} ¬(∃f:ℕ → A. ∀n:ℕ. P [f(n + 1), f n/y, z])

H; \begin{matrix}x : A; \\ f : \{g | y : \{y : A | P [x/z]\} → B[g, y/f, x]\} \\ ⊢_{ν} B type \end{matrix}

H ⊢_{ν} {f | x:A → B} type (VFT)

Appendix C

Proofs

Lemma C.1 If T ∈ 𝕌_{i} and T ∼ T′ then T = T′ ∈ 𝕌_{i}.

Proof

It is easy to verify that the statement holds for all T if it holds for canonical T. Therefore I assume, without loss of generality, that T is canonical and prove the statement by induction on the structure of T. I show the function and partial type cases, the other cases are similar.

Suppose T ∈ 𝕌_{i} and T ∼ T′ where T ≡ Πx:A.B. Then (for some A′, B′) T′ ⇓ Πx:A′.B′ where A ∼ A′ and B ∼ B′. By induction A = A′ ∈ 𝕌_{i}. Suppose a = a′ ∈ A. Then B[a/x] = B[a′/x] ∈ 𝕌_{i} and B[a′/x] ∼ B′[a′/x] so, by induction, B[a′/x] = B′[a′/x] ∈ 𝕌_{i}. It follows that B[a/x] = B′[a′/x] ∈ 𝕌_{i} so T = T′ ∈ 𝕌_{i}.

Suppose T ∈ 𝕌_{i} and T ∼ T′ where T ≡ \overline{A}. Then (for some A′) T′ ⇓ \overline{A′} where A ∼ A′. Since A ∈ 𝕌_{i}, it follows by induction that A = A′ ∈ 𝕌_{i}. Hence \overline{A} = \overline{A′} ∈ 𝕌_{i} so T = T′ ∈ 𝕌_{i}. □

Theorem 4.3 If T type and T ∼ T′ then T = T′. If t ∈ T and t ∼ t′ then t = t′ ∈ T.

Proof

The first part is shown by a similar argument to Lemma C.1. For the second part, it is easy to verify that the statement holds for all T if it holds for canonical T. Therefore I assume, without loss of generality, that T is canonical and prove the statement by induction on the structure of T. The case where T ≡ 𝕌_{i} follows from Lemma C.1. I show the function and partial type cases, the other cases are similar.

Suppose t ∈ Πx:A.B and t ∼ t′. Then (for some b) t ⇓ λx.b. Therefore (for some b′) t′ ⇓ λx.b′ and b ∼ b′. Suppose a = a′ ∈ A. Then b[a/x] = b[a′/x] ∈ B[a/x] and b[a′/x] ∼ b′[a′/x] so, by induction, b[a′/x] = b′[a′/x] ∈ B[a/x]. It follows that b[a/x] = b′[a′/x] ∈ B[a/x] so t = t′ ∈ Πx:A.B.

Suppose t ∈ \overline{T} and t ∼ t′. Certainly t↓ ⇔ t′↓. Suppose t↓, then t ∈ T. By induction t = t′ ∈ T and hence t = t′ ∈ \overline{T}. □

Lemma C.2 If x appears actively in t and t[e/x]↓ then e↓.

Proof

By induction on the structure of t.

Lemma 4.6 If x appears actively in t then t[e/x] ∼ let x = e in t.

Proof

This proof makes use of Proposition 5.4 and Lemma 5.5, but those facts are shown without using this lemma. I first show that let x = e in t ≤ t[e/x]. Let σ be a substitution such that σ(let x = e in t) and σ(t[e/x]) are closed and suppose, without loss of generality, that σ does not substitute for x. Suppose further that σ(let x = e in t)↓. Then σ(e)↓ so let σ(e) ⇓ v. Note that σ(e) ∼ v. Therefore σ(let x = e in t) ≡ let x = σ(e) in σ(t) ≤ let x = v in σ(t) ≤ σ(t)[v/x] ≤ σ(t)[σ(e)/x] ≡ σ(t[e/x]). Hence let x = e in t ≤ e[t/x].

I now show that t[e/x] ≤ let x = e in t. Again suppose σ(let x = e in t) and σ(t[e/x]) are closed and suppose, without loss of generality, that σ does not substitute for x. Suppose further that σ(t[e/x])↓. Note that the active positions of t are also active in σ(t). By Lemma C.2, σ(e)↓. Thus σ(t[e/x]) ≤ σ(let x = e in t) in a similar manner as before. □

Lemma 4.10 If τ is a type system, then for each op ∈ Types, op(τ) is a type system.

Proof

By tedious verification of all cases. The only subtle argument is for unique valuation: For each operator other than VFun, the existential prefix’s instances are uniquely given by τ, T and T′. (Parametric variables (e.g., β_{(−)}) are unique over the membership of α.) Therefore ϕ is uniquely given for operators other than VFun.

For the VFun operator the term P is not uniquely given by τ, T and T′. The relations <, ρ_{(−,−)}, and γ_{(−)} also are not, but they are unique when also given P. Further, it is not obvious that δ_{(−,−)} is uniquely given by τ, T and T′, but again it is clearly unique when also given P. Suppose ϕ and ϕ′ are different equalities resulting from terms P and P′. Define < and <′, γ and γ′, and so forth, to be the various relations resulting from P and P′. We wish to show that t ϕ t′ implies t ϕ′ t′. Suppose t ϕ t′. Then t ⇓ λx.b and t′ ⇓ λx.b′. It remains to show that b[a/x] δ_{(t,a)} b′[a′/x] implies b[a/x] δ′_{(t,a)} b′[a′/x] whenever a α a′. However, δ_{(t,a)} and δ′_{(t,a)} are identical relations when they are both defined, so it suffices to show that δ′_{(t,a)} is defined for all a α a. This may be shown by induction using the order <′. □

Lemma 4.11 If

• τ is type symmetric and type transitive, and

• τ defines only universes (that is, if τT T ϕ then T ⇓ 𝕌_{i} for some i), and

• Close(τ)T_{1}T′_{1}ϕ_{1} and Close(τ)T_{2}T′_{2}ϕ_{2}, and

• either T_{1} ≡ T_{2} or T_{1} ≡ T′_{2},

then either τT_{1}T′_{1}ϕ_{1} and τT_{2}T′_{2}ϕ_{2}, or for some op ∈ Types, op(Close(τ))T_{1}T′_{1}ϕ_{1} and op(Close(τ))T_{2}T′_{2}ϕ_{2}.

Proof

By inspection.

Lemma 4.12 If τ is a type system and if τ defines only universes, then Close(τ) is a type system.

Proof

All but unique valuation and type transitivity are directly by Lemma 4.10 and induction. Unique valuation and type transitivity are by Lemma 4.11 (to ensure both antecedents are from the same case), then by Lemma 4.10 and induction. □

Lemma 4.13 For all i, Univ_{i} and Nuprl_{i} are type systems.

Proof

By complete induction on i.

Theorem 5.1 There exist inadmissible types.

Proof

This example is due to Smith [92]. Let the type T and the function f be defined as follows:

T =^{def} Σh:(ℕ → \overline{ℕ}). ((Πx:ℕ. h x in! ℕ) → Void)
f =^{def} λp.⟨g, λy. ⋆⟩
g =^{def} λx. if x ≤ 0 then 0 else π_{1}(p)(x − 1)

It is easy to verify that \overline{T} type. We wish to show that f has type \overline{T} →\overline{T}. Suppose t = t′ ∈ \overline{T}. We need g[t/p] = g[t′/p] ∈ ℕ → \overline{ℕ} and λy. ⋆ ∈ (Πx:ℕ. (g[t/p])x in! ℕ) → Void. The former is easily shown; to show the latter, I assume that (g[t/p])n converges for every natural number n and draw a contradiction. It follows that Πx:ℕ. (g[t/p])x in! ℕ is empty and λy. ⋆ is vacuously a function from any empty type to Void. Suppose (g[t/p])n converges for every natural number n. Then the term if n ≤ 0 then 0 else π_{1}(t)(n − 1) also converges for every natural number n. It follows that t↓, since π_{1}(t)(0)↓, and hence t ∈ T. Thus (Πx:ℕ. π_{1}(t)(x) in! ℕ)→Void is inhabited (by π_{2}(t)) and consequently it cannot be the case that π_{1}(t)(n)↓ for every natural number n. But this is a contradiction since π_{1}(t)(n − 1)↓ for every n > 1. Therefore f ∈ \overline{T} → \overline{T}.

However, it is not the case that fix(f) ∈ \overline{T}. Suppose fix(f) ∈ \overline{T}. Then fix(f) ∈ T since fix(f) converges (in two steps). Thus π_{2}(fix(f)) ∈ (Πx:ℕ. π_{1}(fix(f))(x) in! ℕ)→Void, which implies that π_{1}(fix(f)) is not total on ℕ, but it is easy to show by induction that π_{1}(fix(f)) is in fact total (on ℕ). Therefore fix(f) ∉ \overline{T} and hence T is not admissible. □

Lemma 5.5 If e ≤ e′ and t ≤ t′ then e[t/x] ≤ e′[t′/x].

Proof

The proof is a straightforward application of Howe’s method. In order to keep this proof brief, I draw from the terminology and results of Howe’s paper [53] and will not repeat the definitions or lemmas. I write t ⇓^{k} t′ when t ⇓ t′ in k steps and t ⇓^{<k} t′ when t ⇓ t′ in less than k steps. It is sufficient to show that each of the noncanonical operators are extensional.

Case 1: Suppose t ≡ e_{1}e_{2}, t′ ≡ e′_{1}e′_{2} and a are closed. Suppose further that t ⇓^{k} a, e_{i} ≤^{∗} e′_{i} and the induction hypothesis holds (for every closed u_{1}, u_{2} and u, if u_{1} ⇓^{<k} u_{2} and u_{1} ≤^{∗} u then u_{2} ≤^{∗} u). Then e_{1} ⇓^{<k} λx.b and b[e_{2}/x] ⇓^{<k} a. By the induction hypothesis, λx.b ≤^{∗} e′_{1}. By Howe’s Lemma 2, there exists b′ ≥^{∗} b such that e′_{1} ⇓ λx.b′. By Howe’s Lemma 1, b[e_{2}/x] ≤^{∗} b′[e′_{2}/x]. By the induction hypothesis a ≤^{∗} b′[e′_{2}/x]. However, t′ ↦^{∗} b′[e′_{2}/x] so b′[e′_{2}/x] ≤ t′. Consequently a ≤^{∗} t′, as desired. Case 2: Suppose t ≡ case(e, x.e_{1}, x.e_{2}), t′ ≡ case(e′, x.e′_{1}, x.e′_{2}) and a are closed. Suppose further that t ⇓^{k} a, e ≤^{∗} e′, e_{i} ≤^{∗} e′_{i} and the induction hypothesis holds. Then e ⇓^{<k} inj_{j}(b) (for some j = 1 or 2) and e_{j}[b/x] ⇓^{<k} a. By the induction hypothesis, inj_{j}(b) ≤^{∗} e′. By Howe’s Lemma 2, there exists b′ ≥^{∗} b such that e′ ⇓ inj_{j}(b′). By Howe’s Lemma 1, e_{j}[b/x] ≤^{∗} e′_{j}[b′/x]. By the induction hypothesis, a ≤^{∗} e′_{j}[b′/x]. However, t′ ↦^{∗} e′_{j}[v′/x] so e′_{j}[v′/x] ≤ t′. Consequently a ≤^{∗} t′, as desired.

Case 3: Suppose t ≡ π_{i}(e), t′ ≡ π_{i}(e′) and a are closed. Suppose further that t ⇓^{k} a, e ≤^{∗} e′ and the induction hypothesis holds. Then e ⇓^{<k} ⟨b_{1}, b_{2}⟩ and b_{i} ⇓^{<k} a. By the induction hypothesis, ⟨b_{1}, b_{2}⟩ ≤^{∗} e′. By Howe’s Lemma 2, there exist b′_{1} ≥^{∗} b_{1} and b′_{2} ≥^{∗} b_{2} such that e′ ⇓ ⟨b′_{1}, b′_{2}⟩. By the induction hypothesis, a ≤^{∗} b′_{i}. However, t′ ↦ b′_{i} so a ≤^{∗} t′. Case 4: Suppose t ≡ (e_{1} =_{A} e_{2}), t′ ≡ (e′_{1} =_{A} e′_{2}) and a are closed. Suppose further that t ⇓^{k} a, e_{i} ≤^{∗} e′_{i} and the induction hypothesis holds. Then, for some atoms α_{1} and α_{2}, e_{i} ⇓^{<k} α_{i}. If α_{1} = α_{2} then a ≡ true and otherwise a ≡ false. By the induction hypothesis, α_{i} ≤^{∗} e′_{i}. By Howe’s Lemma 2, e′_{i} ⇓ α_{i}. Hence t′ ⇓ a, so a ≤^{∗} t′. The cases for =_{Z}, ≤_{Z} and ⊕ are similar. Case 5: Suppose t ≡ let x = e_{1} in e_{2}, t′ ≡ let x = e′_{1} in e′_{2} and a are closed. Suppose further that t ⇓^{k} a, e_{1} ≤^{∗} e′_{1}, e_{2} ≤ e′_{2} and the induction hypothesis holds. Then e_{1} ⇓^{<k} v and e_{2}[v/x] ⇓^{<k} a. By the induction hypothesis, v ≤^{∗} e′_{1}. By Howe’s Lemma 2, there exists v′ ≥^{∗} v such that e′_{1} ⇓ v′. Then by Howe’s Lemma 1, e_{2}[v/x] ≤^{∗} e′_{2}[v′/x]. By the induction hypothesis, a ≤^{∗} e′_{2}[v′/x]. However, t′ ↦^{∗} e′_{2}[v′/x] so a ≤^{∗} t′. Case 6: Suppose t ≡ fix(e), t′ ≡ fix(e′) and a are closed. Suppose further that t ⇓^{k} a, e ≤^{∗} e′ and the induction hypothesis holds. Then e ⇓^{<k} λx.b and b[fix(e)/x] ⇓^{<k} a. By the induction hypothesis, λx.b ≤^{∗} e′. By Howe’s Lemma 2, there exists b′ ≥^{∗} b such that e′ ⇓ λx.b′. By Howe’s Lemma 1, b[fix(e)/x] ≤^{∗} b′[fix(e′)/x], since fix(e) ≤^{∗} fix(e′). By the induction hypothesis, a ≤^{∗} b′[fix(e′)/x]. However, t′ ↦ b′[fix(e′)/x] so a ≤^{∗} t′. □

Lemma 5.7 If e_{1}[t/x] ↦ e_{2}, and e_{1}[t/x] is closed, and t is closed and noncanonical, then either

• there exists e′_{2} such that for any closed t′, e_{1}[t′/x] ↦ e′_{2}[t′/x], or

• there exist e′_{1} and t′ such that e_{1} ≡ e′_{1}[x/y], t ↦ t′ and for any closed t′′, e′_{1}[t′′, t/x, y] ↦

e′_{1}[t′′, t′/x, y]. Proof

Suppose e_{1} ≡ x. Then t ≡ e_{1}[t/x] ↦ e_{2}. Let e′_{1} = y and t′ = e_{2}. Then e′_{1}[t′′, t/x, y] ≡ t ↦ t′ ≡ e′_{1}[t′′, t′/x, y]. The remaining cases are by induction on the derivation of e_{1}[t/x] ↦ e_{2}. I show the lambda rules; the other cases are similar. Suppose the rule used is:

(λz.b)a ↦ b[a/z]

The term e_{1}[t/x] must have the form of a lambda abstraction applied to an argument. Thus e_{1} must be of the form (λz.b)a, since e_{1} ≡ x is already handled and e_{1} ≡ x a is impossible because t is noncanonical. Let e′_{2} = b[a/z] and suppose t′ is closed. Then:

e_{1}[t′/x] ≡ (λz.b[t′/x])(a[t′/x])
↦ b[t′/x](a[t′/x]/z)
≡ b[a/z][t′/x]  (since t′ is closed)
≡ e′_{2}[t′/x]

Suppose the rule used is:

{f ↦ f′ \atop f a ↦ f′ a}

Then it must be the case that e_{1} is of the form f_{1} a (since e_{1} ≡ x is already handled) and f_{1}[t/x] ↦ f_{2} (for some f_{2}). Hence the induction hypothesis holds for f_{1}. Suppose the first case holds: there exists f′_{2} such that for any closed t′, f_{1}[t′/x] ↦ f′_{2}[t′/x]. Let e′_{2} = f′_{2} a and suppose t′ is closed. Then:

e_{1}[t′/x] ≡ (f_{1}[t′/x])(a[t′/x])
↦ (f′_{2}[t′/x])(a[t′/x])
≡ e′_{2}[t′/x]

Suppose the second case holds: there exist f′_{1} and t′ such that f_{1} ≡ f′_{1}[x/y], t ↦ t′ and for any closed t′′, f′_{1}[t′′, t/x, y] ↦ f′_{1}[t′′, t′/x, y]. Let e′_{1} = f′_{1} a and suppose t′′ is closed. Then:

e′_{1}[t′′, t/x, y] ≡ (f′_{1}[t′′, t/x, y])(a[t′′, t/x, y])
≡ (f′_{1}[t′′, t/x, y])(a[t′′, t′/x, y]) (since y is not free in a)
↦ (f′_{1}[t′′, t′/x, y])(a[t′′, t′/x, y])
≡ e′_{1}[t′′, t′/x, y]

□

Lemma 5.8 For all f, e_{1} and e_{2} (where f is closed and x is the only free variable of e_{1}), there exist j and e′_{2} such that if e_{1}[fix(f)/x] ↦^{∗} e_{2} then e_{2} ≡ e′_{2}[fix(f)/x] and for all k ≥ j, e′_{2}[f^{k−j}/x] ≤ e_{1}[f^{k}/x]. Proof

I show the lemma for evaluations of length exactly one. The result then follows by induction on the length of the evaluation sequence, summing the numbers j. Use Lemma 5.7. Suppose the first case holds: there exists e′_{2} such that for any closed t, e_{1}[t/x] ↦ e′_{2}[t/x]. Then e_{1}[fix(f)/x] ↦ e′_{2}[fix(f)/x] and, for any k, e_{1}[f^{k}/x] ↦ e′_{2}[f^{k}/x]. Thus e′_{2}[f^{k−0}/x] ≤ e_{1}[f^{k}/x]. Suppose the second case holds: there exists e′_{1} such that e_{1} ≡ e′_{1}[x/y], and for any closed t, e′_{1}[t, fix(f)/x, y] ↦ e′_{1}[t, f(fix(f))/x, y]. Let e′_{2} = e′_{1}[f x/y]. Then e_{1}[fix(f)/x] ↦ e′_{2}[fix(f)/x]. Suppose k ≥ 1, then e′_{2}[f^{k−1}/x] ≡ e′_{1}[f^{k−1}, f^{k}/x, y] ≤ e′_{1}[f^{k}, f^{k}/x, y] ≡ e_{1}[f^{k}/x]. □

Theorem 5.9 For all f, t and e (where f is closed), if ∀j. e[f^{j}/x] ≤ t, then e[fix(f)/x] ≤ t. Proof

By induction on l that for all f, t and e (where f is closed), (∀j. e[f^{j}/x] ≤ t) ⇒ e[fix(f)/x] ≤_{l} t. The result follows by the definition of ≤. The basis is trivial. Assume the induction hypothesis for l and ∀j. e[f^{j}/x] ≤ t. Let σ be a substitution such that σ(e[fix(f)/x]) and σ(t) are closed and suppose, without loss of generality, that σ does not substitute for x. Then σ(e[fix(f)/x]) ≡ σ(e)[fix(f)/x], σ(e[f^{j}/x]) ≡ σ(e)[f^{j}/x] (for any j), and the only free variable of σ(e) is x. Suppose σ(e[fix(f)/x]) ⇓ e′. By Lemma 5.8, e′ ≡ e′′[fix(f)/x] and, for some j and all k ≥ j, e′′[f^{k−j}/x] ≤ σ(e)[f^{k}/x]. Then, by assumption and transitivity, ∀k ≥ j. e′′[f^{k−j}/x] ≤ σ(t). Therefore, changing variables to replace k − j with k, e′′[f^{k}/x] ≤ σ(t) for any k. Let e′′ = θ(x⃗_{1}.t_{1}, …, x⃗_{n}.t_{n}) (and suppose, without loss of generality, that x does not appear in any x⃗_{i}). Then σ(t) ⇓ θ(x⃗_{1}.t′_{1}, …, x⃗_{n}.t′_{n}) where, for 1 ≤ i ≤ n and any k, t_{i}[f^{k}/x] ≤ t′_{i}. By induction, t_{i}[fix(f)/x] ≤_{l} t′_{i}, for any i. Therefore σ(e[fix(f)/x]) ≤_{l+1} σ(t) and hence e[fix(f)/x] ≤_{l+1} t. □

Lemma 5.18

• Adm(A + B | y : S) if ∀s ∈ S. (A + B)[s/y] type and Adm(A | y : S) and Adm(B | y : S).

• Adm(Σx:A.B | y : S) if ∀s ∈ S. (Σx:A.B)[s/y] type and Σy:S.A type and Adm(A | y : S) and Adm(B[π_{1}(z), π_{2}(z)/y, x] | z : (Σy:S.A))

• Adm(Void | y : S), Adm(Atom | y : S), Adm(ℤ | y : S) and Adm(𝔼 | y : S)

• Adm(a_{1} = a_{2} in A | y : S) if ∀s ∈ S. (a_{1} = a_{2} in A)[s/y] type and Adm(A | y : S)

• Adm(a_{1} ≤ a_{2} | y : S)

• Adm(\overline{A} | y : S) if ∀s ∈ S. \overline{A}[s/y] type and Adm(A | y : S)

• Adm(a in! A | y : S) if ∀s ∈ S. (a in! A)[s/y] type

Proof I show the product and equality cases; the other cases are similar but easier.

Case 1: For the product case, let f, t, t′ and e be arbitrary. Suppose e^{[ω]} ∈ S and j is such that ∀k ≥ j. e^{[k]} ∈ S ∧ t^{[k]} = t^{′[k]} ∈ (Σx:A.B)[e^{[k]}/y]. It is necessary to show that t^{[ω]} = t^{′[ω]} ∈ (Σx:A.B)[e^{[ω]}/y]. Since e^{[ω]} ∈ S, it follows that (Σx:A.B)[e^{[ω]}/y] type. Both t^{[j]} and t^{′[j]} converge to pairs, so, by Corollary 5.6, t^{[ω]} ⇓ ⟨a, b⟩ and t^{′[ω]} ⇓ ⟨a′, b′⟩ for some terms a, b, a′ and b′. To get that b = b′ ∈ B[e^{[ω]}/y][a/x], it suffices to show that π_{2}(t^{[ω]}) = π_{2}(t^{′[ω]}) ∈ B[e^{[ω]}/y][π_{1}(t^{[ω]})/x]. Rearranging, it suffices to show equality in B[π_{1}(z), π_{2}(z)/y, x][⟨e, π_{1}(t)⟩^{[ω]}/z].

Since Adm(B[π_{1}(z), π_{2}(z)/y, x] | z : (Σy:S.A)), it suffices to show that ⟨e, π_{1}(t)⟩^{[ω]} ∈ Σy:S.A and ∀k ≥ j. ⟨e, π_{1}(t)⟩^{[k]} ∈ Σy:S.A ∧ π_{2}(t^{[k]}) = π_{2}(t^{′[k]}) ∈ B[π_{1}(z), π_{2}(z)/y, x][⟨e, π_{1}(t)⟩^{[k]}/z]. The former will follow from a ∈ A[e^{[ω]}/y] and the supposition. The left half of the latter also follows from the supposition. Rearranging the right half, it suffices to show that π_{2}(t^{[k]}) = π_{2}(t^{′[k]}) ∈ B[e^{[k]}/y][π_{1}(t^{[k]})/x], which follows from the supposition. The proof that a = a′ ∈ A[e^{[ω]}/y] is similar but easier. Hence t^{[ω]} = t^{′[ω]} ∈ (Σx:A.B)[e^{[ω]}/y].

Case 2: For the equality case, again let f, t, t′ and e be arbitrary. Suppose e^{[ω]} ∈ S and j is such that ∀k ≥ j. e^{[k]} ∈ S ∧t^{[k]} = t^{′[k]} ∈ (a_{1} = a_{2} in A)[e^{[k]}/y]. It is necessary to show that t^{[ω]} = t^{′[ω]} ∈ (a_{1} = a_{2} in A)[e^{[ω]}/y]. Since e^{[ω]} ∈ S, it follows that (a_{1} = a_{2} in A)[e^{[ω]}/y]type. Both t^{[j]} and t^{′[j]} converge to ⋆, so, by Corollary 5.6, t^{[ω]} and t^{′[ω]} converge to ⋆. It remains to show that a_{1}[e^{[ω]}/y] = a_{2}[e^{[ω]}/y] ∈ A[e^{[ω]}/y]. Since Adm(A|y : S), it suffices to show that ∀k ≥ j. e^{[k]} ∈ S ∧ a_{1}[e^{[k]}/y] = a_{2}[e^{[k]}/y] ∈ A[e^{[k]}/y]. This follows since (a_{1} = a_{2} in A)[e^{[k]}/y] is inhabited for all k ≥ j. □

Lemma 5.20 Adm(Πx:A.B | y : S) if ∀s ∈ S. (Σx:A.B)[s/y] type and WCoAdm(A | y : S) and ∀s ∈ S, a ∈ A[s/y]. Adm(B[a/x] | y : S)

Proof Let f, t, t′ and e be arbitrary. Suppose e^{[ω]} ∈ S and j is such that ∀k ≥ j. e^{[k]} ∈ S ∧ t^{[k]} = t^{′[k]} ∈ (Πx:A.B)[e^{[k]}/y]. I need to show that t^{[ω]} = t^{′[ω]} ∈ (Πx:A.B)[e^{[ω]}/y]. Since e^{[ω]} ∈ S, it follows that (Πx:A.B)[e^{[ω]}/y] type. Both t^{[j]} and t^{′[j]} converge to lambda abstractions, so, by Corollary 5.6, t^{[ω]} ⇓ λx.b and t^{′[ω]} ⇓ λx.b′ for some terms b and b′. Suppose a = a′ ∈ A[e^{[ω]}/y]. To get that b[a/x] = b′[a′/x] ∈ B[e^{[ω]}, a/y, x] it suffices to show that t^{[ω]}a = t^{′[ω]}a′ ∈ B[e^{[ω]}, a/y, x].

Since e^{[ω]} ∈ S, it follows that Adm(B[a/x] | y : S). Therefore, it suffices to show that for some j′ and all k ≥ j′, t^{[k]}a = t^{′[k]}a′ ∈ B[e^{[k]}, a/y, x]. Since WCoAdm(A | y : S), there exists j′′ such that ∀k ≥ j′′. a = a′ ∈ A[e^{[k]}/y]. Therefore j′ = max(j, j′′) suffices. □

Lemma 5.21

• A + B is (weakly) coadmissible for y in S if ∀s ∈ S. (A + B)[s/y] type and A and B are (weakly) coadmissible for y in S

• WCoAdm(Σx:A.B | y : S) if ∀s ∈ S. (Σx:A.B)[s/y] type and WCoAdm(A | y : S) and ∀s ∈ S, a ∈ A[s/y]. WCoAdm(B[a/x] | y : S)

• CoAdm(Σx:A.B|y : S) if ∀s ∈ S. (Σx:A.B)[s/y]type and Σy:S.Atype and CoAdm(A|y : S) and CoAdm(B[π_{1}(z), π_{2}(z)/y, x] | z : (Σy:S.A))

• Void, Atom, ℤ and 𝔼 are strongly or weakly coadmissible for y in any S

• CoAdm(a_{1} = a_{2} in A | y : S) if ∀s ∈ S. (a_{1} = a_{2} in A)[s/y] type and CoAdm(A | y : S)

• a_{1} ≤ a_{2} is strongly or weakly coadmissible for y in any S

• \overline{A} is (weakly) coadmissible for y in S if ∀s ∈ S. \overline{A}[s/y] type and A is (weakly) coadmissible for y is S

• a in! A is strongly or weakly coadmissible for y in S if ∀s ∈ S. (a in! A)[s/y] type

Proof The proof is largely similar to the preceding proofs, but inverted. I show the proofs for full coadmissibility of products and partial types.

Case 1: For the product case, let f, t, t′ and e be arbitrary. Suppose e^{[ω]} ∈ S, j is such that ∀k ≥ j. e^{[k]} ∈ S, and t^{[ω]} = t^{′[ω]} ∈ (Σx:A.B)[e^{[ω]}/y]. It is necessary to show that there exists j′ such that ∀k ≥ j′. t^{[k]} = t^{′[k]} ∈ (Σx:A.B)[e^{[k]}/y]. For any k ≥ j, (Σx:A.B)[e^{[k]}/y] type. Both t^{[ω]} and t^{′[ω]} converge to pairs, so, by compactness, there exists some j′′ such that for all k ≥ j′′, t^{[k]} and t^{[k]} converge to pairs. Thus it suffices to show that for some j′ ≥ max(j, j′′) and all k ≥ j′, π_{1}(t^{[k]}) = π_{1}(t^{′[k]}) ∈ A[e^{[k]}/y] and π_{2}(t^{[k]}) = π_{2}(t^{′[k]}) ∈ B[e^{[k]}, π_{1}(t^{[k]})/y, x]. I show the latter; the former is similar.

Rearranging, it suffices to show equality in B[π_{1}(z), π_{2}(z)/y, x][⟨e, π_{1}(t)⟩^{[k]}/z]. By coadmissibility, it suffices to show ⟨e, π_{1}(t)⟩^{[ω]} ∈ Σy:S.A and ∃j′′′. ∀k ≥ j′′′. ⟨e, π_{1}(t)⟩^{[k]} ∈ Σy:S.A and π_{2}(t^{[ω]}) = π_{2}(t^{′[ω]}) ∈ B[π_{1}(z), π_{2}(z)/y, x][⟨e, π_{1}(t)⟩^{[ω]}/z]. The first follows from the supposition and the second will follow from π_{1}(t^{[k]}) ∈ A[e^{[k]}/y] and the supposition. Rearranging the third, is suffices to show that π_{2}(t^{[ω]}) = π_{2}(t^{′[ω]}) ∈ B[e^{[ω]}/y][π_{1}(t^{[ω]})/x], which follows from the supposition.

Case 2: For the partial type case, let f, t, t′ and e be arbitrary. Suppose e^{[ω]} ∈ S, j is such that ∀k ≥ j. e^{[k]} ∈ S, and t^{[ω]} = t^{′[ω]} ∈ \overline{A}[e^{[ω]}/y]. I need to show that there exists j′ such that ∀k ≥ j′. t^{[k]} = t^{′[k]} ∈ \overline{A}[e^{[k]}/y]. For any k ≥ j, \overline{A}[e^{[k]}/y] type. Suppose t^{[ω]} does not converge. (Note the non-constructivity of this argument.) Then t^{′[ω]} does not converge and neither does t^{[k]} or t^{′[k]} for any k (since t^{[k]} ≤ t^{[ω]} and t^{′[k]} ≤ t^{′[k]} for all k). Thus for all k ≥ j, t^{[k]} = t^{′[k]} ∈ \overline{A}[e^{[k]}/y].

Suppose t^{[ω]} converges. Then t^{[ω]} = t^{′[ω]} ∈ A[e^{[ω]}/y]. By coadmissibility, there exists j′ such that ∀k ≥ j′. t^{[k]} = t^{′[k]} ∈ A[e^{[k]}/y]. Hence ∀k ≥ max(j, j′). t^{[k]} = t^{′[k]} ∈ \overline{A}[e^{[k]}/y]. □

Lemma 5.23 (Predicate-admissibility and weak and full coadmissibility of case analysis.) Proof

The proof follows the same lines as those of Lemmas 5.18 and 5.21. Lemma 5.27 The type {x : A | B} is admissible if:

• Adm(A), and

• Adm(B | x : A), and

• there exists b such that b[a/x] ∈ B[a/x] whenever a ∈ A and ∃b′. b′ ∈ B[a/x]. Proof

Let f, t and t′ be arbitrary. Suppose j is such that ∀k ≥ j. t^{[k]} = t^{′[k]} ∈ {x : A | B}. Since {x : A | B} is inhabited it is a type. Since Adm(A), t^{[ω]} = t^{′[ω]} ∈ A. By set membership, for all k ≥ j there exists b′ such that b′ ∈ B[t^{[k]}/x]. Thus b[t^{[k]}/x] ∈ B[t^{[k]}/x] for all k ≥ j. Hence b[t^{[ω]}/x] ∈ B[t^{[ω]}/x] follows by predicate-admissibility. □

Lemma 5.29 (Predicate-admissibility of set types.) Proof

The proof follows the same lines as Lemma 5.27. Lemma 5.30 (Weak and full coadmissibility and monotonicity of set types.) Proof

The proof follows the same lines as Lemma 5.21 and Proposition 5.26. Lemma 5.31 (Admissibility, predicate-admissibility, weak and full coadmissibility and monotonicity of quotient types.) Proof

The proof follows the same lines as the proofs for the set type. Theorem 5.32 There does not exist an algorithm that computes an integer j such that ∀k ≥ j. t = t′ ∈ \overline{T} [e^{[k]}/x], when given S, T, f, t, t′, e and i such that:

• ∀s ∈ S. \overline{T} [s/x] type

• CoAdm(T | x : S)

• e^{[ω]} ∈ S

• ∀k ≥ i. e^{[k]} ∈ S

• t = t′ ∈ \overline{T} [e^{[ω]}/x] Proof

Suppose such an algorithm exists. Let g be an arbitrary term that computes a total function on integers; that is, g ∈ ℤ → ℤ. Given the algorithm, we may effectively determine whether g iterated on 1 ever computes 0, which is certainly undecidable. Let f = λh. λn. if n =_{Z} 0 then 0 else h(gx) and let h = fix(f). Note that f ∈ \overline{ℤ → \overline{ℤ}} → ℤ → \overline{ℤ} and h ∈ ℤ → \overline{ℤ}. By construction, g iterated on 1 computes 0 if and only if h(1)↓.

We will use the algorithm to determine an upper bound on the number of recursive calls needed to simulate h. Let

S = \overline{ℤ}
T = x in! ℤ
f = as above
t, t′ = let y = h(1) in ⋆
e = w(1)
i = 0

Observe that e^{[ω]} = h(1) and e^{[k]} = (f^{k})(1), so the first four preconditions of the algorithm are satisfied. Moreover, CoAdm(T | x : S) can be shown constructively. For the final precondition, suppose t↓. Then h(1)↓ so t ∈ h(1) in! ℤ.

Therefore let j be the result computed by the algorithm. I show that f^{j}(1)↓ exactly when h(1)↓. Since f^{j}(1) approximates h(1), it follows that f^{j}(1)↓ implies h(1)↓. By the algorithm specification, t ∈ \overline{f^{j}(1) in! ℤ}. If h(1)↓ then t↓, so t ∈ f^{j}(1) in! ℤ and consequently f^{j}(1)↓.

Let h′ = \overbrace{f(f ⋯ f}^{j times}(λy.1) ⋯), and observe that f^{j}(1)↓ exactly when h′(1) ⇓ 0. Consequently h(1)↓ exactly when h′(1) ⇓ 0. However, h′ is total, so we may decide whether h(1)↓ by running h′(1). □

Bibliography

[1] Martín Abadi and Luca Cardelli. A theory of primitive objects: Untyped and first-order systems. In Theoretical Aspects of Computer Software, volume 789 of Lecture Notes in Computer Science, pages 296–320. Springer-Verlag, April 1994.

[2] Martín Abadi and Luca Cardelli. A Theory of Objects. Springer-Verlag, 1996.

[3] Martín Abadi, Luca Cardelli, and Ramesh Viswanathan. An interpretation of objects and object types. In Twenty-Third ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages, pages 296–409, St. Petersburg, Florida, January 1996.

[4] Peter Aczel. Frege structures revisited. In B. Nordström and J. Smith, editors, Proceedings of the 1983 Marstrand Workshop, 1983.

[5] Stuart Allen. A non-type-theoretic definition of Martin-Löf’s types. In Second IEEE Symposium on Logic in Computer Science, pages 215–221, Ithaca, New York, June 1987.

[6] Stuart Allen. A Non-Type-Theoretic Semantics for Type-Theoretic Language. Ph.D. dissertation, Department of Computer Science, Cornell University, Ithaca, New York, 1987.

[7] Philippe Audebaud. Partial objects in the calculus of constructions. In Sixth IEEE Symposium on Logic in Computer Science, pages 86–95, Amsterdam, July 1991.

[8] Bruno Barras, Samuel Boutin, Cristina Cornes, Judicaël Courant, Jean-Christophe Filliâtre, Eduardo Giménez, Hugo Herbelin, Gérard Huet, César Muñoz, Chetan Murthy, Catherine Parent, Christine Paulin-Mohring, Amokrane Saïbi, and Benjamin Werner. The Coq Proof Assistant Reference Manual. INRIA-Rocquencourt, CNRS and ENS Lyon, 1996.

[9] David A. Basin. An environment for automated reasoning about partial functions. In Ninth International Conference on Automated Deduction, volume 310 of Lecture Notes in Computer Science. Springer-Verlag, 1988.

[10] Michael Beeson. Recursive models for constructive set theories. Annals of Mathematical Logic, 23:127–178, 1982.

[11] Lars Birkedal and Robert Harper. Relational interpretations of recursive types in an operational setting. In Theoretical Aspects of Computer Software, 1997.

[12] Val Breazu-Tannen, Thierry Coquand, Carl A. Gunter, and Andre Scedrov. Inheritance as implicit coercion. Information and Computation, 93:172–221, 1991.

[13] Kim B. Bruce. A paradigmatic object-oriented programming language: Design, static typing and semantics. Journal of Functional Programming, 4(2):127–206, April 1994.

[14] Kim B. Bruce, Luca Cardelli, and Benjamin C. Pierce. Comparing object encodings. In Theoretical Aspects of Computer Software, Sendai, Japan, September 1997.

[15] Peter Canning, William Cook, Walter Hill, John Mitchell, and Walter Olthoff. F-bounded quantification for object-oriented programming. In Conference on Functional Programming Languages and Computer Architecture, pages 273–280, September 1989.

[16] Luca Cardelli. Phase distinctions in type theory. Unpublished manuscript, January 1988.

[17] Luca Cardelli. Structural subtyping and the notion of power type. In Fifteenth ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages, pages 70–79, San Diego, January 1988.

[18] Luca Cardelli. Typeful programming. In Formal Description of Programming Concepts. Springer-Verlag, 1991.

[19] R.L. Constable, S.F. Allen, H.M. Bromley, W.R. Cleaveland, J.F. Cremer, R.W. Harper, D.J. Howe, T.B. Knoblock, N.P. Mendler, P. Panangaden, J.T. Sasaki, and S.F. Smith. Implementing Mathematics with the Nuprl Proof Development System. Prentice-Hall, 1986.

[20] Robert L. Constable. Intensional analysis of functions and types. Technical Report CSR-118-82, Department of Computer Science, University of Edinburgh, June 1982.

[21] Robert L. Constable. Constructive mathematics as a programming logic I: Some principles of theory. In Topics in the Theory of Computation, volume 24 of Annals of Discrete Mathematics, pages 21–37. Elsevier, 1985. Selected papers of the International Conference on Foundations of Computation Theory 1983.

[22] Robert L. Constable. Type theory as a foundation for computer science. In Theoretical Aspects of Computer Software 1991, volume 526 of Lecture Notes in Computer Science, pages 226–243, Sendai, Japan, 1991. Springer-Verlag.

[23] Robert L. Constable and Karl Crary. Computational complexity and induction for partial computable functions in type theory. Technical report, Department of Computer Science, Cornell University, 1997.

[24] Robert L. Constable and Scott Fraser Smith. Partial objects in constructive type theory. In Second IEEE Symposium on Logic in Computer Science, pages 183–193, Ithaca, New York, June 1987.

[25] Robert L. Constable and Scott Fraser Smith. Computational foundations of basic recursive function theory. In Third IEEE Symposium on Logic in Computer Science, pages 360–371, Edinburgh, Scotland, July 1988.

[26] Robert L. Constable and Daniel R. Zlatin. The type theory of PL/CV3. ACM Transactions on Programming Languages and Systems, 6(1):94–117, January 1984.

[27] Thierry Coquand. An analysis of Girard’s paradox. In First IEEE Symposium on Logic in Computer Science, pages 227–236, Cambridge, Massachusetts, June 1986.

[28] Thierry Coquand. Metamathematical investigations of a calculus of constructions. In P. Odifreddi, editor, Logic and Computer Science, volume 31 of The APIC Series, pages 91–122. Academic Press, 1990.

[29] Thierry Coquand and Gérard Huet. The calculus of constructions. Information and Computation, 76:95–120, 1988.

[30] Karl Crary. Foundations for the implementation of higher-order subtyping. In 1997 ACM SIGPLAN International Conference on Functional Programming, pages 125–135, Amsterdam, June 1997.

[31] Karl Crary. Simple, efficient object encoding using intersection types. Technical Report TR98-1675, Department of Computer Science, Cornell University, April 1998.

[32] N. G. de Bruijn. A survey of the project Automath. In J. P. Seldin and J. R. Hindley, editors, To H.B. Curry: Essays on Combinatory Logic, Lambda-Calculus and Formalism, pages 579–606. Academic Press, 1980.

[33] Michael Dummett. Elements of Intuitionism. Oxford Logic Guides. Clarendon Press, 1977.

[34] Andrzej Filinski. Controlling Effects. Ph.D. dissertation, Carnegie Mellon University, School of Computer Science, Pittsburgh, Pennsylvania, May 1996.

[35] Jean-Yves Girard. Une extension de l’interprétation de Gödel à l’analyse, et son application à l’élimination de coupures dans l’analyse et la théorie des types. In J. E. Fenstad, editor, Proceedings of the Second Scandinavian Logic Symposium, pages 63–92. North-Holland Publishing Co., 1971.

[36] Jean-Yves Girard. Interprétation fonctionelle et élimination des coupures de l’arithmétique d’ordre supérieur. Ph.D. dissertation, Université Paris VII, 1972.

[37] Jean-Yves Girard, Yves Lafont, and Paul Taylor. Proofs and Types. Cambridge University Press, 1988.

[38] Michael J. Gordon, Arthur J. Milner, and Christopher P. Wadsworth. Edinburgh LCF: A Mechanised Logic of Computation, volume 78 of Lecture Notes in Computer Science. Springer-Verlag, 1979.

[39] Michael J. C. Gordon and Tom F. Melham. Introduction to HOL: A Theorem Proving Environment for Higher-Order Logic. Cambridge University Press, 1993.

[40] Robert Harper. Constructing type systems over an operational semantics. Journal of Symbolic Computation, 14:71–84, 1992.

[41] Robert Harper. Personal communication, 1993.

[42] Robert Harper, Furio Honsell, and Gordon Plotkin. A framework for defining logics. Journal of the ACM, 40(1):143–184, January 93.

[43] Robert Harper and Mark Lillibridge. Explicit polymorphism and CPS conversion. In Twentieth ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages, pages 206–219, January 1993.

[44] Robert Harper and Mark Lillibridge. A type-theoretic approach to higher-order modules with sharing. In Twenty-First ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages, pages 123–137, Portland, Oregon, January 1994.

[45] Robert Harper and John C. Mitchell. On the type structure of Standard ML. ACM Transactions on Programming Languages and Systems, 15(2):211–252, April 1993.

[46] Robert Harper, John C. Mitchell, and Eugenio Moggi. Higher-order modules and the phase distinction. In Seventeenth ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages, pages 341–354, San Francisco, January 1990.

[47] Robert Harper and Robert Pollack. Type checking with universes. Theoretical Computer Science, 89:107–136, 1991.

[48] Robert Harper and Chris Stone. A type-theoretic interpretation of Standard ML. In Proof, Language and Interaction: Essays in Honour of Robin Milner. The MIT Press, 1998. To appear.

[49] Jason J. Hickey. Formal objects in type theory using very dependent types. In Foundations of Object Oriented Languages 3, 1996.

[50] Jason J. Hickey. A semantics of objects in type theory. Unpublished manuscript, 1997.

[51] W. Howard. The formulas-as-types notion of construction. In J. P. Seldin and J. R. Hindley, editors, To H.B. Curry: Essays on Combinatory Logic, Lambda-Calculus and Formalism, pages 479–490. Academic Press, 1980.

[52] Douglas J. Howe. The computational behaviour of Girard’s paradox. In Second IEEE Symposium on Logic in Computer Science, pages 205–214, Ithaca, New York, June 1987.

[53] Douglas J. Howe. Equality in lazy computation systems. In Fourth IEEE Symposium on Logic in Computer Science, 1989.

[54] Douglas J. Howe. Semantic foundations for embedding HOL in Nuprl. Technical report, Bell Labs, 1996.

[55] Shigeru Igarashi. Admissibility of fixed-point induction in first-order logic of typed theories. Technical Report AIM-168, Computer Science Department, Stanford University, May 1972.

[56] Paul Jackson. The Nuprl Proof Development System, Version 4.1. Department of Computer Science, Cornell University, 1994.

[57] Paul Bernard Jackson. Enhancing the Nuprl Proof Development System and Applying it to Computational Abstract Algebra. Ph.D. dissertation, Department of Computer Science, Cornell University, Ithaca, New York, January 1995.

[58] Samuel Kamin. Inheritance in Smalltalk-80: A denotational definition. In Fifteenth ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages, pages 80–87, San Diego, January 1988.

[59] Christoph Kreitz. Formal reasoning about communications systems I. Technical report, Department of Computer Science, Cornell University, 1997.

[60] John Launchbury and Simon L. Peyton Jones. State in Haskell. Lisp and Symbolic Computation, 8(4):293–341, December 1995.

[61] Xavier Leroy. Unboxed objects and polymorphic typing. In Nineteenth ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages, pages 177–188, 1992.

[62] Xavier Leroy. Manifest types, modules and separate compilation. In Twenty-First ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages, pages 109–122, Portland, Oregon, January 1994.

[63] Xavier Leroy. Applicative functors and fully transparent higher-order modules. In Twenty-Second ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages, San Francisco, January 1995.

[64] Xavier Leroy. The Objective Caml System, Release 1.00. Institut National de Recherche en Informatique et Automatique (INRIA), 1996.

[65] David MacQueen. Using dependent types to express modular structure. In Thirteenth ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages, pages 277–286, St. Petersburg Beach, Florida, January 1986.

[66] David B. MacQueen and Mads Tofte. A semantics for higher-order functors. In Fifth European Symposium on Programming, volume 788 of Lecture Notes in Computer Science, pages 409–423. Springer-Verlag, 1994.

[67] Per Martin-Löf. An intuitionistic theory of types: Predicative part. In Proceedings of the Logic Colloquium, 1973, volume 80 of Studies in Logic and the Foundations of Mathematics, pages 73–118. North-Holland, 1975.

[68] Per Martin-Löf. Constructive mathematics and computer programming. In Sixth International Congress of Logic, Methodology and Philosophy of Science, volume 104 of Studies in Logic and the Foundations of Mathematics, pages 153–175. North-Holland, 1982.

[69] Paul Francis Mendler. Inductive Definition in Type Theory. Ph.D. dissertation, Department of Computer Science, Cornell University, Ithaca, New York, September 1987.

[70] Albert R. Meyer and Mark B. Reinhold. ‘Type’ is not a type. In Thirteenth ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages, pages 287–295, St. Petersburg Beach, Florida, January 1986.

[71] Robin Milner, Mads Tofte, and Robert Harper. The Definition of Standard ML. The MIT Press, Cambridge, Massachusetts, 1990.

[72] Robin Milner, Mads Tofte, Robert Harper, and David MacQueen. The Definition of Standard ML (Revised). The MIT Press, Cambridge, Massachusetts, 1997.

[73] 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.

[74] Eugenio Moggi. Notions of computation and monads. Information and Computation, 93:55–92, 1991.

[75] Greg Morrisett. Compiling with Types. Ph.D. dissertation, Carnegie Mellon University, School of Computer Science, Pittsburgh, Pennsylvania, December 1995.

[76] Greg Morrisett, Karl Crary, Neal Glew, and David Walker. Stack-based typed assembly language. In Second Workshop on Types in Compilation, March 1998.

[77] Greg Morrisett, David Walker, Karl Crary, and Neal Glew. From System F to typed assembly language. In Twenty-Fifth ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages, pages 85–97, San Diego, January 1998. Extended version published as Cornell University technical report TR97-1651.

[78] Alan Mycroft. Polymorphic type schemes and recursive definitions. In Sixth International Symposium on Programming, number 167 in Lecture Notes in Computer Science, pages 217–228. Springer-Verlag, April 1984.

[79] Chris Okasaki. Catenable double-ended queues. In 1997 ACM SIGPLAN International Conference on Functional Programming, pages 66–74, Amsterdam, June 1997.

[80] Erik Palmgren. An information system interpretation of Martin-Löf’s partial type theory with universes. Information and Computation, 106:26–60, 1993.

[81] Erik Palmgren and Viggo Stoltenberg-Hansen. Domain interpretations of intuitionistic type theory. U.U.D.M. Report 1989:1, Uppsala University, Department of Mathematics, January 1989.

[82] Simon L. Peyton Jones and Philip Wadler. Imperative functional programming. In Twentieth ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages, Charleston, South Carolina, January 1993.

[83] Benjamin C. Pierce and David N. Turner. Simple type-theoretic foundations for objectoriented programming. Journal of Functional Programming, 4(2):207–247, April 1994.

[84] Didier Rémy. Programming objects with ML-ART: An extension to ML with abstract and record types. In Theoretical Aspects of Computer Software 1994, volume 789 of Lecture Notes in Computer Science, pages 321–346, Sendai, Japan, 1994. Springer-Verlag.

[85] Didier Rémy and Jérôme Vouillon. Objective ML: A simple object-oriented extension of ML. In Twenty-Fourth ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages, pages 40–53, Paris, January 1997.

[86] John C. Reynolds. The essence of Algol. In J. W. de Bakker and J. C. van Vliet, editors, Proceedings of the International Symposium on Algorithmic Languages, pages 345–372, Amsterdam, October 1981. North-Holland.

[87] Adrian Rezus. Semantics of constructive type theory. Technical Report 70, Informatics Department, Faculty of Science, Nijmegen, University, The Netherlands, September 1985.

[88] Dana Scott. Outline of a mathematical theory of computation. In Fourth Princeton Conference on Information Sciences and Systems, pages 169–176, 1970.

[89] Dana Scott. Lattice theoretic models for various type-free calculi. In Fourth International Congress of Logic, Methodology and Philosophy of Science. North-Holland, 1972.

[90] Dana Scott and Christopher Strachey. Toward a mathematical semantics for computer languages. In Proceedings of the Symposium on Computers and Automata, volume 21 of Microwave Research Institute Symposia Series. Polytechnic Institute of Brooklyn, 1971.

[91] Scott F. Smith. Hybrid partial-total type theory. International Journal of Foundations of Computer Science, 6:235–263, 1995.

[92] Scott Fraser Smith. Partial Objects in Type Theory. Ph.D. dissertation, Department of Computer Science, Cornell University, Ithaca, New York, January 1989.

[93] D. Tarditi, G. Morrisett, P. Cheng, C. Stone, R. Harper, and P. Lee. TIL: A typedirected optimizing compiler for ML. In 1996 ACM SIGPLAN Conference on Programming Language Design and Implementation, pages 181–192, May 1996.

[94] Philip Wadler. Comprehending monads. In Mathematical Structures in Computer Science, volume 2, pages 461–493. Cambridge University Press, 1992.

[95] Philip Wadler. The essence of functional programming. In Nineteenth ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages, Albuquerque, New Mexico, January 1992.

[96] Alfred North Whitehead and Bertrand Russell. Principia Mathematica. Cambridge University Press, 2nd edition, 1910.