A Theory of Program Refinement

Ewen W.K.C. Denney

Doctor of Philosophy University of Edinburgh

Do mo phàrantan

Abstract

We give a canonical program refinement calculus based on the lambda calculus and classical first-order predicate logic, and study its proof theory and semantics. The intention is to construct a metalanguage for refinement in which basic principles of program development can be studied.

The idea is that it should be possible to induce a refinement calculus in a generic manner from a programming language and a program logic. For concreteness, we adopt the simply-typed lambda calculus augmented with primitive recursion as a paradigmatic typed functional programming language, and use classical first-order logic as a simple program logic.

A key feature is the construction of the refinement calculus in a modular fashion, as the combination of two orthogonal extensions to the underlying programming language (in this case, the simply-typed lambda calculus).

The crucial observation is that a refinement calculus is given by extending a programming language to allow indeterminate expressions (or ‘stubs’) involving the construction ‘some program x such that P ’. Factoring this into ‘some x …’ and ‘… such that P ’, we first study extensions to the lambda calculus providing separate analyses of what we might call ‘true’ stubs, and structured specifications. The questions we are concerned with in these calculi are how do stubs interact with the programming language, and what is a suitable notion of structured specification for program development.

The full refinement calculus is then constructed in a natural way as the combination of these two subcalculi. The claim that the subcalculi are orthogonal extensions to the lambda calculus is justified by a result that a refinement can actually be factored into simpler judgements in the subcalculi, that is, into logical reasoning and simple decomposition.

The semantics for the calculi are given using Henkin models with additional structure. Both simply-typed lambda calculus and first-order logic are interpreted using Henkin models themselves. The two subcalculi require some extra structure and the full refinement calculus is modelled by Henkin models with a combination of these extra requirements. There are soundness and completeness results for each calculus, and by virtue of there being certain embeddings of models we can infer that the refinement calculus is a conservative extension of both of the subcalculi which, in turn, are conservative extensions of the lambda calculus.

Acknowledgements

Thanks to Gordon Plotkin and John Power for supervising this thesis and for providing advice and encouragement. I hope their suffering over my writing has not been in vain. Marcelo Fiori also supervised the early stages. Thanks also to John for his famous chats.

I had useful conversations with Alex Bunkenburg, Joe Morris, Álvaro Moreira, Masahito Hasegawa, Thomas Kleymann, David Aspinall, and Jitka Stříbrná.

This work was supported by an EPSRC studentship and, in the final year, by a part-time research contract with Gordon Plotkin.

This thesis was examined by Don Sannella and Peter O’Hearn. Thanks to them for all their helpful comments.

Thanks to Yukki and my parents for their support. Doh jia agus mòran taing.

Declaration

I declare that this thesis was composed by myself and that the work contained therein is my own, except where explicitly stated otherwise in the text.

(Ewen W.K.C. Denney)

Table of Contents

Chapter 1 Introduction 4

1.1 Refinement Methodology . . . . . . . . . . . . . . . . . . . . . . . 5 1.1.1 Stepwise Development . . . . . . . . . . . . . . . . . . . . 6 1.1.2 Programming Knowledge . . . . . . . . . . . . . . . . . . . 10

1.2 Program Logics and Specification . . . . . . . . . . . . . . . . . . 12 1.3 Calculi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

1.3.1 Refinement Terms . . . . . . . . . . . . . . . . . . . . . . . 15 1.3.2 Refinement Types . . . . . . . . . . . . . . . . . . . . . . . 17 1.3.3 Refinement Calculus . . . . . . . . . . . . . . . . . . . . . 18

1.4 Other Methodologies . . . . . . . . . . . . . . . . . . . . . . . . . 19 1.5 Choices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 1.6 Related Work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

1.6.1 Refinement Terms . . . . . . . . . . . . . . . . . . . . . . . 23 1.6.2 Refinement Types . . . . . . . . . . . . . . . . . . . . . . . 25 1.6.3 Refinement Calculi . . . . . . . . . . . . . . . . . . . . . . 27

1.7 Summary of Thesis . . . . . . . . . . . . . . . . . . . . . . . . . . 28 1.8 Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

Chapter 2 Preliminaries 32

2.1 Simply-typed Lambda Calculus . . . . . . . . . . . . . . . . . . . 32 2.1.1 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.1.2 λ^{×→}-Axiom Systems . . . . . . . . . . . . . . . . . . . . . 35 2.1.3 Booleans and Naturals . . . . . . . . . . . . . . . . . . . . 35

2.2 Models of Simply-typed Lambda Calculus . . . . . . . . . . . . . 39 2.3 First-order Logic of Simply-typed Lambda Calculus . . . . . . . . 44 2.4 Models of First-order Logic . . . . . . . . . . . . . . . . . . . . . 48

Chapter 3 Refinement Terms 52 3.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 3.2 The Calculus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.2.1 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.2.2 Judgements . . . . . . . . . . . . . . . . . . . . . . . . . . 55 3.2.3 λ_{?}-Axiom Systems . . . . . . . . . . . . . . . . . . . . . . 55

3.3 Metatheory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 3.4 Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 3.5 First-order Logic of Simply-typed Refinement . . . . . . . . . . . 83 3.6 Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

Chapter 4 Refinement Types 88 4.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 4.2 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 4.3 The Calculus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

4.3.1 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 4.3.2 Judgements . . . . . . . . . . . . . . . . . . . . . . . . . . 96 4.3.3 λ_{(:)}-Axiom Systems . . . . . . . . . . . . . . . . . . . . . . 97 4.3.4 Rules of the Calculus . . . . . . . . . . . . . . . . . . . . . 99 4.3.5 Booleans and Naturals . . . . . . . . . . . . . . . . . . . . 111 4.3.6 Metatheory . . . . . . . . . . . . . . . . . . . . . . . . . . 114

4.4 Division by 2 Revisited . . . . . . . . . . . . . . . . . . . . . . . . 119 4.5 Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 4.6 Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134

Chapter 5 Refinement Calculus 135 5.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 5.2 The Calculus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

5.2.1 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 5.2.2 Judgements . . . . . . . . . . . . . . . . . . . . . . . . . . 139 5.2.3 λ_{⊑}-Axiom Systems . . . . . . . . . . . . . . . . . . . . . . 139 5.2.4 Rules of the Calculus . . . . . . . . . . . . . . . . . . . . . 140

5.3 An Example of Refinement . . . . . . . . . . . . . . . . . . . . . . 154 5.4 Comparisons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

5.4.1 Extended ML . . . . . . . . . . . . . . . . . . . . . . . . . 159 5.4.2 Aspinall’s λ_{ASL+} . . . . . . . . . . . . . . . . . . . . . . . 160 5.4.3 Type Theory . . . . . . . . . . . . . . . . . . . . . . . . . 161 5.4.4 Lego . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 5.4.5 Refinement Calculus of Back, Morgan and Morris . . . . . 163

5.5 Metatheory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 5.6 Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 5.6.1 Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 5.6.2 λ_{⊑}-Henkin Models . . . . . . . . . . . . . . . . . . . . . . 179

5.7 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192

Chapter 6 Conclusions and Further Work 193

6.1 Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 6.1.1 Refinement Terms . . . . . . . . . . . . . . . . . . . . . . . 195 6.1.2 Refinement Types . . . . . . . . . . . . . . . . . . . . . . . 195

6.2 Technical Extensions and Conjectures . . . . . . . . . . . . . . . . 196 6.3 Operational Semantics . . . . . . . . . . . . . . . . . . . . . . . . 199

6.3.1 Refinement Terms . . . . . . . . . . . . . . . . . . . . . . . 199 6.3.2 Refinement Types . . . . . . . . . . . . . . . . . . . . . . . 202

6.4 Annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202 6.5 Search Calculi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 6.6 Logical Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 6.7 Second Order: Data Refinement . . . . . . . . . . . . . . . . . . . 205 6.8 Full Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206 6.9 Program Transformation . . . . . . . . . . . . . . . . . . . . . . . 206 6.10 Abstract Viewpoint . . . . . . . . . . . . . . . . . . . . . . . . . . 207 6.11 Aspects of the Software Life-cycle . . . . . . . . . . . . . . . . . . 208

6.11.1 Prototyping . . . . . . . . . . . . . . . . . . . . . . . . . . 208 6.11.2 Maintenance . . . . . . . . . . . . . . . . . . . . . . . . . . 209 6.11.3 Reverse Engineering . . . . . . . . . . . . . . . . . . . . . 209

Appendix A Notation 210

Bibliography 211

Chapter 1

Introduction

Program refinement is a programming methodology in which a formal description of what a program should do — a specification — is gradually refined into an executable program satisfying that specification.

This thesis is a study of program refinement for a simple idealised programming and specification language. Although our analysis is theoretical, we give motivation from practical considerations.

The kind of issues with which we are concerned are:

• What logical machinery and semantic principles are involved in program refinement?

• Are there interesting fragments of refinement calculus which have practical uses?

• Given a program logic, what is a suitable specification language based on it for program refinement?

• Understanding the general relationship between a refinement calculus, a programming language, and a program logic.

• How might the structure of a calculus inform the architecture of tools for program development?

Although we pose these questions here in general terms, for concreteness we use typed λ-calculus as a paradigmatic functional programming language, and classical first-order logic as a program logic. It is our hope that by making clear how these choices affect our analysis, we will attain some degree of generality. The significance of these decisions is discussed below.

In order to address these questions, we carry out a modular analysis of a simple refinement calculus. We suggest that a refinement calculus can be understood as a combination of two extensions to the underlying programming language — one accounting for specifications, and the other for what we call ‘pure’ refinement.

Just as the lambda calculus can be used as a metalanguage for studying functional programming languages, so the lambda calculus based refinement calculus we develop could be used as a metalanguage for studying refinement in functional programming languages. The interest in studying metalanguages is that they provide a simple setting in which fundamental issues can be studied.

The purpose of this analysis then is not to justify or consider refinement as a viable methodology, nor to develop an industrial strength refinement calculus^{1}, but to fully work out the theory of refinement in one limited area. Ultimately this knowledge could help to delimit the boundary between manual and automated program development and so be used in theorem provers.

Certainly, a theory of specification and programming is a necessary precursor to having tools which support automatic or manual refinement and synthesis. We will suggest later how the theory can inform the construction of a tool.

In this introduction we give an overview of the issues involved and how our refinement calculus is built up. We start with a description of the refinement methodology of program development in Section 1.1, then in Section 1.2 describe the related notion of program verification in terms of a simple satisfaction relation between programs and specifications, and the relevance of program logics for refinement. Then we will see that there are essentially two themes in refinement — structure on specifications, and internalisation of development — and this is discussed in Section 1.3, where we give an overview of the calculi in this thesis. In Section 1.4 we compare the particular form of refinement studied here with other methodologies. In Section 1.5 we discuss the significance of the choices we have made for programming language and program logic. Then in Sections 1.6 and 1.7 we describe related work and summarise the thesis.

1.1 Refinement Methodology

We now describe how program refinements are constructed, and consider what features a calculus should have in order to formalise refinement. There are two uses for refinement. First, it is a methodology for the construction of correct programs. Second, refinement can form the basis of a framework in which programming knowledge can be presented (as collections of refinements).

1A contradiction in terms, some would say!

1.1.1 Stepwise Development

When programmers write programs they often use a mixture of top-down and bottom-up development. Central to the method of top-down development is the idea of a stub. Suppose we are writing some program, the main body of which needs to use a sorting function on lists. We prefer to get the structure of the main body correct first before writing the sorting function, but in order to get the program to compile, or even to type-check, we must at least declare the sorting function, and give it a dummy body — a stub.

                         fun Sort l = nil;

Any use of the function elsewhere is now well-defined

                        l′ = Sort some list;

Conceptually, however, this seems rather inelegant. Practically, although the program will compile and will run, the programmer must always bear in mind that this is not ‘true’ code. When testing the behaviour of the partially written program this must be taken into account. One way of avoiding behaviour which depends on dummy bodies would be to use some kind of error or exception mechanism as a stub. This is not entirely satisfactory, however, since it is possible that the programmer might forget that various stubs are left throughout the program. It would be better to have a language construct that allows true stubs to be written. The sorting function would be written as

                        fun Sort l =?_{int list};

The idea is that ?_{int list} stands for some unwritten code of type int list. This can then be type-checked and the system would give a warning message to the effect that certain lines of code still remain to be implemented. We would like to be able to compile and run such partially written programs. In general, this is not possible since our program might just be one big stub. However, we would like an understanding of programs with stubs which lets us do this whenever possible.

Now, this much is a true description of (part of) what programmers do in practice. However, it is little more than a convenient notation for recording the development of a program through a sequence of abstractions. The essence of refinement lies in being able to use the full expressive power of a program logic. Rather than just have a stub for ‘some program’ we use the logic to say ‘some program such that P ’. So whereas above we could do no more than just specify the type of a program using ?_{τ}, we now extend this to ?_{(x:τ)P}, meaning ‘some program x such that P ’, where proposition P can contain free variable x of type τ. For example, suppose we have defined predicates Sorted and Permutation in first-order logic. We can then write

            fun Sort l =?_{(l′:int list) Sorted (l′) ∧ Permutation (l,l′)} ;

This should be thought of as a description of some program which takes a list l and returns a list l′ such that l′ is sorted and a permutation of l, and not as a nondeterministic function which returns all such l′. This contrast between nondeterminism and, what we will refer to as underdeterminism, is of central importance in this thesis and is discussed in more detail below.

We can think of these combinations of logical specifications with program code as a kind of ‘abstract program’. Languages which combine program and specification constructs in this way are called wide-spectrum languages.

Now, we proceed by doing a case analysis on the input. If the input is the nil list then clearly we just return the nil list. If it is of the form x :: xs then we must decide how this is to be sorted. We can decompose the sortedness of a nonempty list into the correct insertion of some element into a sorted list, since,

Sorted (l_{1}) ∧ Sorted (l_{2}) ∧ l_{1} ≤ x ∧ x ≤ l_{2} ⊃ Sorted (l_{1} ++ (x :: l_{2}))

Let us write Insertion (l, x, l′) for the proposition that the list l′ is the insertion of x into list l, that is,

(∃l_{1} : intlist . ∃l_{2} : intlist . l = l_{1} ++ l_{2} ∧ l_{1} ≤ x ∧ x ≤ l_{2} ∧ l′ = l_{1} ++ (x :: l_{2})

Using the above proposition, we can prove that

             Sorted (l) ∧ Insertion (l, x, l′) ⊃ Sorted (l′)

Using this idea, we replace the specification of sorting with this more constructive form.

fun Sort l =

?(l′ : intlist) l = nil ⊃ l′ = nil ∧ ∃x : int . ∃xs : intlist.l = x :: xs ⊃ ∃xs′ : intlist.Sorted(xs′)∧Permutation(xs, xs′)∧Insertion(xs′, x, l′)

Now, this specification has a recursive form. Assuming the existence of a refinement rule which lets us replace a recursive specification with a recursion over a specification, we aim to shift the recursion from specification to code. This is more clearly explained by the example. Let us write Insert for the specification

(f : intlist → int → intlist) ∀l : intlist . ∀x : int . Insertion (l, x, f l x)

After introducing the recursion, the sorting specification is

                fn l : intlist ⇒ listrec (nil,?_{Insert}, l)

This is a specification of insertion sort. Although this may seem rather a large step, we will cover a similar example in more detail in Chapter 5.

Thus, we decompose the specification into the simpler specification of an insertion function, and some code to carry out the recursion over the list.

The development so far can be represented as a process of refinement of a piece of ‘unwritten code’ corresponding to the original specification, which we summarise as

 ?_{Sort} ⊑ fn l : intlist ⇒

?(l′ : intlist) l = nil ⊃ l′ = nil ∧ ∃x : int . ∃xs : intlist . l = x :: xs ⊃ ∃xs′ : intlist.Sorted(xs′) ∧ Permutation(xs, xs′) ∧ Insertion (xs′, x, l′)

      ⊑ fn l : intlist ⇒ listrec (nil,?_{Insert}, l)

This hierarchical separation of concerns illustrates the stepwise refinement methodology of program development. The idea is to gradually implement the specification in stages. At each stage, we either simplify a specification, possibly by decomposing it into a combination of subspecifications, or introduce some program code. Thus refinement can be seen as consisting of two alternating phases. On the one hand, logical specifications are replaced with something equivalent or more specific, so that they are more amenable to implementation. On the other, there are decomposition rules, where a specification is split into a number of simpler specifications while introducing a program constructor.

Now, although refinement is an inherently top-down methodology, there is also a bottom-up aspect. We sometimes want to use programs which have already been written, from a library say. So-called problem reduction is when a program is used to directly implement a specification. For example, we might have written the insertion function before, or be able to take it from a library. We illustrate this by simply assuming a free variable

                   ins : intlist → int → intlist

for which ∀l : intlist . ∀x : int . Insertion (l, x, ins l x).

The final step in the refinement then is

ins : intlist → int → intlist
⊢ λl : intlist . listrec (nil,?_{insert}, l) ⊑ λl : intlist . listrec (nil, ins, l)

This is typical of the general form of programming problems. In practice, the question we ask is given a library of component programs, how can we implement a specification? Even if we start with an empty context, during decomposition of a specification we will often construct subprograms which can be used elsewhere.

We can represent this formally by a global context of assumptions of the form x : τ |P, meaning that program x has type τ and that proposition P (which can contain x) holds. For example, the insertion function is assumed as:

ins : intlist → int → intlist |
                   ∀l : intlist . ∀x : int . Insertion (l, x, ins l x)

The final step in the refinement above follows from the following simple rule for problem reduction:

x : τ |P ⊢ ?_{(x:τ)P} ⊑ x

Two important characteristics of refinement are that it is stepwise and piecewise. By stepwise, we mean that the specification can be refined into a program in a number of small steps, while piecewise means that a large specification can be refined one piece at a time, in the knowledge that this leads to a valid refinement of the whole specification. This means that the calculus should have rules so that the refinement relation is transitive and compositional (with respect to the program constructors). Moreover, the refinement rules should be syntax-directed (as far as this is possible).

There are some other features we would like in a refinement calculus. If we can place requirements on the output, it is convenient to also make assumptions about the input. For example, the specification of a search function might assume that the input list was sorted. This could be expressed with a notation like

              fun Search l n where Sorted(l)

= ?_{(b:bool) b=true ⟺ In(n,l)} ;

We are allowed to use the fact that l is sorted when implementing the body. However, (l : int list) Sorted (l) is not a type of the programming language We must still produce a function which works for all arguments of type int list. In particular, any implementation of this specification must produce a result for unsorted lists. The point is, however, that for the purpose of implementing the specification it does not matter what the result is for unsorted lists. In a sense, we can regard two implementations t and t′ as being the same when they give the same results for sorted lists. We can write this as

                    t =_{((l:intlist) Sorted (l))→int→bool} t′

where we think of (l : intlist) Sorted (l) → int → bool as the specification of functions from (l : intlist) Sorted (l) and int to bool, and of the equality as being ‘at the specification’.

Now, the intention behind annotating stubs with logical properties is, of course, that they will eventually be replaced with code satisfying that property. The slogan of the refinement methodology is that it is correctness preserving — programs will be automatically correct by construction. By requiring that each refinement step preserve properties, the program eventually constructed in a chain of refinement steps from a specification will be guaranteed to satisfy the original specification. The proof of correctness has been carried out essentially as a sideeffect of the program’s construction. This is easier than verifying the completed program independent of its construction.

However, if we start a refinement from a mixture of specification and code, then what is a correct refinement? Indeed, what does it mean for the individual rules to be correct? This is related to the question of how the specifications, logic and refinement all relate to each other. It is often most natural to specify a program using a mixture of logic and algorithm. We will discuss specifications in more detail below.

Just as we want to be able to reason about programs, so we would like to be able to reason about abstract programs. We can formalise all of this by defining a notion of satisfaction of specifications by programs, and extending this to abstract programs. As this is all carried out in a context of assumptions, the form of the satisfaction judgement is

x_{1} sat (x : τ_{1})P_{1}, …, x_{n} sat (x : τ_{n})P_{n} ⊢ r sat (x : τ)P

Then to say that a refinement rule, Γ ⊢ r ⊑ r′, is correct means: for all specifications (x : τ)P, if Γ ⊢ r sat (x : τ)P then Γ ⊢ r′ sat (x : τ)P.

The notation for refinement is suggestive of the fact that refinement is an inequality. This raises the question of the relationship it bears to the underlying equality^{2} of the programming language. In general, we will address questions of conservativity of refinement calculi over program logics and the equational theory of a programming language.

In addition we would like completeness of refinement with respect to the logic, if at all possible: if Γ ⊢ t sat (x : τ)P then we should be able to obtain the program by refinement: Γ ⊢ ?_{(x:τ)P} ⊑ t.

1.1.2 Programming Knowledge

Although some researchers have portrayed refinement as a framework for eventual automated programming tools, this should not be seen as the sole selling point of the refinement methodology. There is no doubt that constructing a program by refinement involves more work than just writing it directly. The point is that the discipline this imposes offers a framework in which programming as a whole can be more easily carried out. We feel that this aspect of refinement has not been sufficiently emphasised in the literature.

2That is, some notion of equivalence of programs.

Actual programming practice involves a sequence of decisions, in which the programmer figures out how to solve some problem. In figuring out how to implement a specification, the programmer will use a number of insights in order to (informally) justify these decisions. This sequence of decisions is usually discarded, just leaving the final program. Such an approach is reasonable if the program is only intended for consumption by computer. However, program comprehension is necessary whenever a program is intended to be processed by humans. Moreover, there are two activities for which it is essential — verification and maintenance. The difficulty in understanding programs arises from the need to rediscover the insights that the programmer used in writing the program in the first place, and so it would be best if these were retained. Hence we are led to study a paradigm of programming in which derivations are primary. Scherlis and Scott [SS83] discuss the need for a logic of programming, as distinct from a logic of programs. An early advocate of refinement and its use for program comprehension was Wirth [Wir71].

The idea of explaining a program by its refinement can, in principle, be applied to program optimisation. Although an executable program may consist of optimised ‘spaghetti’, there will be a level of structured code above this in the derivation. The relationship between the two is justified by some optimising transformations.

The particular knowledge of some application domain can be collected in a library of programs, each paired with its specification, prog sat spec. This can be viewed as a refinement, spec ⊑ prog, but this does not give all the information that might be useful to a programmer. It is more insightful to read such relationships as a sequence of abstractions. Similarly, for teaching purposes, algorithms and general programming principles can often be best explained as a refinement through several levels of abstraction.

The knowledge of experienced programmers is essentially having solutions to generic problems and how to apply them in particular situations. Empirical studies have shown that programming knowledge can be encapsulated as collections of derivations [SE84].

To a certain extent, modern programming languages encourage programmers to indicate levels of abstraction through the use of abstract data types and structured programming methods, but it is not always possible to express all the structure in the program text. The refinement paradigm is applicable to all programming languages and development methodologies.

1.2 Program Logics and Specification

The specification of a computer program is a formal description of the essential properties it is to have. Correctness of a program means that it satisfies some intended specification and verification is the task of establishing correctness. Verifying that a program satisfies a specification is, in some sense, dual to refining specifications to programs.

Program properties can be expressed in a number of ways. We will first consider logics in which the propositions refer to programs — program logics. The term ‘program logic’ is often used specifically for the first-order dynamic logic of Harel [Har79] (see the survey in [vL90] for example), but we use it more generally, for any logic of programs (although we will only consider one ourselves). Harel [Har80] gives a survey of different techniques for proving program correctness.

However, a basic distinction can be drawn between extensional and intensional properties. Extensional (or functional) properties are those concerned with a program’s input-output behaviour, that is, by viewing it as a black box. Formally, we can say that they are the properties preserved under extensional equality (which has a suitable inductive definition). Intensional properties, on the other hand, depend on the structure of the program itself. Examples of such notions include complexity, program style, and so on. Here we will be concerned with extensional properties and say more about this later in Section 1.4.

Program analysis [Nie96] is the task of taking a program and finding which, of a specific class of properties, it satisfies. These are typically computational in nature, such as strictness properties, or binding times.

A specification, on the other hand, is generally of more complex properties. For concreteness, let us use the classical first-order equational theory of simplytyped λ-calculus as a program logic. We will lay the basis of this in Chapter 2, and give an outline here. The pre-expressions are:

Types  τ ::= 1 | γ | τ × τ | τ → τ
Terms  t ::= x | k(t_{1}, …, t_{n}) | ∗ | ⟨t, t′⟩ | λx : τ.t | π_{i}(t) | tt′
Propositions  P ::= ⊥ | P ⊃ P′ | ∀x : τ.P | F (t_{1}, …, t_{n}) | t =_{τ} t′
Variable contexts  Γ ::= ⟨⟩ | Γ, x : τ
Propositional contexts  Δ ::= ⟨⟩ | Δ, P

This is with respect to a collection of ground types γ, constants k, and primitive predicates F (extensional predicates such as Iszero and Even).

There are well-formedness judgements

Γ ⊢ t : τ
Γ ⊢ P wf

and a proof judgement

Γ; Δ ⊢ P

meaning: for all values in the types of Γ, if each proposition in Δ holds then P holds.

Now, the next question is how to write specifications using the program logic. The simplest choice is to write (x : τ)P, where x is allowed to be free in P, for the specification of the property of the program x of type τ such that P holds.

The satisfaction judgement can be formulated, then, as

Γ; Δ ⊢ t sat (x : τ)P

meaning: for all values in the types of Γ, if each proposition in Δ holds then t has type τ and P [t/x] holds.

Now either we take this judgement as derived, and an abbreviation for

Γ ⊢ t : τ and Γ; Δ ⊢ P [t/x]

or we can axiomatise it directly on the structure of P, and have this correspondence as a theorem. For example, we would have rules like

Γ; Δ ⊢ t sat (x : τ)P Γ; Δ ⊢ t sat (x : τ)P′
Γ; Δ ⊢ t sat (x : τ)P ∧ P′

This is really just an alternative presentation of the logic, so we do not consider it when we give the satisfaction rules.

It is also possible to have rules on the structure of the terms, such as

Γ; Δ ⊢ t sat (x : τ)P  Γ; Δ ⊢ t′ sat (y : τ′)P′
Γ; Δ ⊢ ⟨t, t′⟩ sat (z : τ × τ′)P [π_{1}(z)/x] ∧ P′[π_{2}(z)/y]

This is in the spirit of the refinement types which we introduce later as our notion of specification.

In Chapter 2 we will give a set-theoretic semantics to the logic (making the extensionality clear) and prove it sound and complete.

The real difference between a specification and a proposition of a program logic, however, is that a specification possesses structure ‘in the large’ []. It is important to structure specifications in order to handle the large scale and complexity involved in real systems. Structure allows us to reason about specifications in a compositional way and, in particular, to carry out component-wise refinement.

We can distinguish two approaches to structuring specifications — algebraic and type-theoretic. The distinction is best illustrated by considering how a datatype would be specified. There is a signature of basic types and operations, and axioms over this signature. The axioms are given using some base logic, so specifications are constructed on top of this. This generates a theory for the datatype consisting of all theorems provable from the axioms. In algebraic specification the generated theory is regarded as the specification, and structuring of specifications takes place at the level of the theory. For example, we might take the union of two theories.

This theory-level structuring should be contrasted with structuring the signature itself. In type theory, signatures with axioms can be given as existential types, and then specifications are combined using the type-theoretic constructors. For example, we can take the product of two specifications. The axioms are given within the type theory itself.

In Section 1.3.2 below we will suggest a third approach which combines aspects of both algebraic and type-theoretic specification.

1.3 Calculi

We observed in the discussion of refinement in Section 1.1 that the basic construct in the calculus is some means of expressing “some x such that P ”. We can factor this into “some x” and “… such that P ”, and study separate extensions of λ^{×→} with each construct. We believe these extensions to be of independent interest providing, respectively, analyses of ‘pure’ refinement and of structured specifications.

Coming from another direction, we would expect refinement to subsume verification, and so should be able to extend the verification calculus of Section 1.2 in some minimal way to get a refinement calculus. We can ask the question, given a satisfaction system, what are the minimum additions needed to get a refinement calculus? There are essentially two things which need to be added — a notion of structured proof, so that structure can be transferred from the proof to a program, and a means of internalising backwards search. These two additions correspond to the extended calculi which we have mentioned:

• Structured proofs — refinement types

• Internalisation of backwards search — refinement terms.

1.3.1 Refinement Terms

So we first develop a simple equational theory of refinement based on a lambda calculus with true stubs. Rather than use the ML-like notation of fun Sort l = ?_{int list}, we will write the lambda term λl : int list.?_{int list}. We will call lambda terms with the possibility of such stubs, refinement terms, and refer to this possibility of terms only being partially determined as underdeterminism. We are careful to make a distinction between underdeterminism and nondeterminism, which we regard as a computational, as opposed to a specificational, phenomenon. For example, we do not regard abstractions with underdetermined body as being determined, which is what some authors are led to do, by viewing terms as being nondeterministic. This difference is not just one of intuition — different axioms are satisfied. Moreover, we could imagine nondeterminism and underdeterminism arising together in a concurrent setting, for example. Then it would be particularly important to maintain a distinction. We will elaborate on these differences in Chapter 3.

Without logic, the calculus may seem too simple to be interesting but it is worth studying for a number of reasons. The full refinement calculus is quite complicated and the subcalculus can act as a stage towards understanding the full system. As we will see, this is justified by virtue of the full calculus being a conservative extension.

Secondly, by not having any logical annotations in the stubs, it is possible to automatically check for well-formedness and, in fact, to evaluate terms in some cases. The calculus could serve as a basis for a simple practical program development system.

There are two forms of judgement in the calculus. Letting r range over refinement terms, we give judgements for typing x_{1} : σ_{1}, …, x_{n} : σ_{n} ⊢ r : τ and refinement x_{1} : σ_{1}, …, x_{n} : σ_{n} ⊢ r ⊑_{τ} r′. Refinement subsumes equality, which can be defined as mutual refinement.

As for the lambda calculus, it would be possible to regard typing as a derived judgement, with r : τ meaning r ⊑_{τ} r′. However, it is clearer to keep the judgements separate.

There are refinement rules for decomposition

?_{σ×τ} ⊑_{σ×τ} ⟨?_{σ},?_{τ}⟩

and problem reduction

Γ, x : τ ⊢ ?_{τ} ⊑_{τ} x

Intuitively, we think of programs either in terms of how they evaluate, or as computing some mathematical value. We will think of abstract programs in terms of how they can be implemented. An intuitive way to think of this is that a term r corresponds to the set of realizers obtained by all possible ways of filling in the stubs with program code. Refinement, then, is the subset relation. We axiomatise refinement so that it is complete with respect to this semantics.

Based on this intuition, we can see that only determined terms can be substituted for variables, as substituting directly would duplicate underdeterminism. For example, the term (λn : nat.⟨n, n⟩)?_{nat} should not be equal to ⟨?_{nat},?_{nat}⟩. However, we do expect (λn : nat.n)?_{nat} = ?_{nat}. For similar reasons to those of the computational lambda calculus [Mog91], this leads us to introduce a ‘let’ construction, albeit with a different axiomatisation than there.

A direct motivation for introducing a let-construct comes from considering the result of combining underdetermined terms. Suppose two programs are being developed one of which depends on the other. Let the partially developed programs be r_{1}[x] and r_{2} where the free variable x is intended to be replaced by the program, the current state of development of which is r_{2}. If we want to consider the system as a whole, so as to prove some property say, then we cannot just substitute r_{2} for x in r_{1}. The combined system can be represented by the let term

                         let x : τ be r_{2} in r_{1}

where τ is the type of r_{2}.

1.3.2 Refinement Types

We give a calculus in which we formalise the specification language and program logic. This task can be phrased in general terms as addressing the question of what is a suitable notion of specification for a programming language, where the properties of interest can be expressed using some given program logic. If we are just interested in input-output relations of programs then classical first-order logic will suffice for program logic.

In Section 1.2, we used the program logic to give specifications directly, but noted in Section 1.1.1 that (x : σ)P → (y : τ)Q is a useful abbreviation for combining specifications. We will write Π_{x:ϕ}ψ for the specification of those functions which for all arguments x which satisfy ϕ return a result which satisfies ψ. This formulation of specifications is useful for reasoning inductively.

The general idea is to use type constructors to combine specifications. Similarly, we write Σ_{x:ϕ}ψ for the specification of pairs such that the left component x satisfies ϕ and the right satisfies ψ.

We also saw in Section 1.1.1 that it is natural to introduce a notion of equality at a specification. This leads us to take specifications as primitive rather than types.

This combination of the program logic with the type theory of the programming language is a form of refinement types. The general idea of refinement types is to have two levels — an underlying level of program types, and a more expressive level of program properties, which are then treated like types. For us, this more expressive level will be the specifications.

This provides an alternative to simply using a program logic, or to using a type theory irrespective of any logic, and we discuss the advantages in Chapter 4.

This simple extension — replacing types by refinement types — affords a considerable degree of conceptual simplicity. Satisfaction of specifications by programs can then be formalised as a (refinement) typing judgement. Rather than write t sat ϕ we regard satisfaction as generalised typing and write t : ϕ.

The terms of the λ-calculus are extended by allowing refinement types in abstractions. This subsumes the idea of indicating assumptions on the input by annotating the types in abstractions.

Contexts consist of variable assumptions of the form x : ϕ. This means that the (refinement) typing judgement formalises the satisfaction of specifications by programs, under the assumption that some other programs satisfy specifications.

It is also convenient, though not essential, to also allow propositions as assumptions in the context.

Finally, we need to define a notion of refinement on specifications, i.e. on refinement types. This is the replacement of a specification by one more logically specific (and not the replacement of stubs by code).

If we just use the program logic, then refinement of specifications is no more than logical implication. With refinement types, however, we must define a refinement judgement, ϕ ⊑_{τ} ϕ′, which we give as a form of subtyping on specifications over a type τ.

1.3.3 Refinement Calculus

We combine the calculi of stubs and specifications to get a logical refinement calculus. The fundamental construct is the logical stub, ?_{ϕ}, where ϕ is a refinement type. Using logical stubs to combine specifications with code gives us a useful means of specifying programs.

For example, using a refinement type Sorted List, we can specify a search function as

                 λn : nat.?_{Π_{l:Sorted List}(b:bool)b=true ⟺ In(l,n)}

The rules of the refinement calculus are the natural generalisations from the subcalculi. The satisfaction of specifications by programs is generalised to abstract programs, with the idea that an abstract program has a property if every program to which it could refine has that property. This lets us ask of partially developed programs what properties they are guaranteed to have, when fully written.

The refinement rules make use of the underlying logic, and just as we introduced a notion of equality at a specification, so we have refinement at a specification. We write r ⊑_{ϕ} r′ for the refinement of r to r′ at refinement type ϕ, but often omit the ϕ when not significant. In fact, the idea of the ϕ not being significant can be made formal.

So in the refinement calculus there are two kinds of refinement, one inherited from each subcalculus. This does reflect programming practice. A partially implemented program, r consists of a mixture of logical specification and program code. At any stage there are two options open: write some more code, replacing a piece of specification with concrete program, or modify some specification, either by replacing it with an equivalent specification, or with a more constrained one.

The connection between logic and refinement is given by the rule

{Γ ⊢ ϕ ⊑ ϕ′ \atop Γ ⊢ ?_{ϕ} ⊑ ?_{ϕ′}}  (1.1)

a particular case of which is

{Γ, x : τ ⊢ P′ ⊃ P \atop Γ ⊢ ?_{(x:τ)P} ⊑ ?_{(x:τ)P′}}

and the rule

{Γ, x : ϕ ⊢ r ⊑_{ψ} r′ \atop Γ ⊢ λx : ϕ.r ⊑_{Π_{x:ϕ}ψ} λx : ϕ.r′}

For example, in the search function specified above, we can use the information that l is sorted, expressed as the refinement typing l : Sorted List, when refining the body.

These are sufficient to derive all uses of the logic in refinement. For example, refinement steps can generate proof obligations such as

?_{(z:σ×τ)P [z]} ⊑ ⟨?_{(x:σ)Q[x]},?_{(y:τ)R[y]}⟩

when

∀x : σ.∀y : τ.Q[x] ∧ R[y] ⊃ P [⟨x, y⟩]

but this can be factored into an instance of (1.1) and decomposition.

To summarise then, we want a calculus where we can prove propositions, P, where we can prove refinement typings, r : ϕ, and where we have correctness preserving refinement rules to prove refinements of the form, r ⊑_{ϕ} r′, which generalise ordinary program equality. So we would like an extension of the simplytyped lambda calculus, where the usual equivalences hold, and in addition, we can express refinements.

We construct the refinement calculus in a modular fashion, as the combination of these two calculi. We are justified in understanding the refinement calculus in this way since it is a conservative extension of both of the subcalculi.

In Figure 1.1 we summarise the important judgements of the different calculi, and indicate the connections between the calculi and their classes of models. The upwards arrows are intended to indicate conservative extensions of calculi, and ‘inclusions’ of models, in the sense of there being a correspondence between the meanings of judgements.

1.4 Other Methodologies

Refinement is just one of many methodologies for formal program development: other approaches include program extraction and deliverables for example. The idea in all of these is to start with a formal description of the behaviour of a

{Γ ⊢ r : τ \atop Γ ⊢ r ⊑_{τ} r′} Refinement Terms  Γ ⊢ t : ϕ ; Γ ⊢ t =_{ϕ} t′ ; Γ ⊢ ϕ ⊑_{τ} ϕ′ Refinement Types

Γ ⊢ r : ϕ ; Γ ⊢ r ⊑_{ϕ} r′ ; Γ ⊢ ϕ ⊑ ϕ′ Refinement Calculus

r refinement terms ϕ refinement types
t determined terms τ types

Judgements

[figure: Theories; Models; refinement calculus; Henkin models with logical factoring; refinement terms; Henkin models with factoring; refinement types; Henkin models with per structure; simply-typed λ-calculus + first-order logic; Henkin Models] Inclusion of Calculi

Figure 1.1: Overview of Calculi program, usually expressed in some logical language, and construct a program which meets that description.

The idea in program refinement is to construct the program in a stepwise manner from the specification, and to have an explicit record of the stage of development in a wide-spectrum language. An advantage of using a wide-spectrum language is that it is often convenient to specify using a mixture of logic and algorithm. Refinement is a formal program development methodology since each refinement step preserves correctness, so the program is guaranteed to meet its description.

It is possible to study refinement via an encoding in a type theory (assuming that the programming language constructs can be suitably encoded — not necessarily the case in the presence of recursion). Luo [Luo91] gives an encoding of data refinement in the Extended Calculus of Constructions. An explicit calculus for refinement, however, has the advantage that it forces us to think directly about the formalism and the semantics.

Now, a contrast can be drawn between program and data refinement. In data refinement, a program written using an abstract data type, such as stacks, is rewritten to use a more concrete data type, lists say. The abstract data type Stack can be seen as a specification, and the implementation by lists, as a refinement of the program. Program refinement on the other hand, starts with a specification of a program, rather than a datatype. In this thesis we are concerned with program refinement.

Some authors see data refinement as being the central concept in program development. This is the basis of the VDM methodology [Jon90] for example. However, we believe that any calculus for data refinement would have to incorporate program refinement anyway, as the stepwise development of a datatype must include the stepwise development of its operations, that is, of programs, and so program refinement seems a natural starting point.

A similar concept to what we have called abstract programs, also used in program development, is that of program skeletons. These are templates of code expressing useful algorithm schemes, such as divide-and-conquer. We can regard refinement terms as a simple formalisation of skeletons, rather than taking them to be some kind of meta-entity. We cannot in general express skeletons using parameterisation, since parameters cannot range over code which can contain local variables.

To many people, “program refinement” and “program transformation” are synonyms. While the basic idea of either can be generalised to include the other, it is useful to draw a distinction between refinement of a logical specification into concrete code, and transformation of concrete program code into ‘better’ code. In this thesis then, our notion of refinement will not incorporate program transformation.

The other key aspect of our approach to refinement is that we work within an equational paradigm, viewing refinement as a kind of generalised (in)equality: spec ⊑ prog. Although equality seems a natural thing to consider when studying lambda calculi, a notion of program equivalence is not primary for reasoning about programs in some languages. It is not so important, for example, in hardware derivation. Nevertheless, we study refinement in this higher-order manner: given two terms, prove one refines the other.

This is not the only possibility however. We could have a search-oriented system where the user starts with the specification and directly refines it to a program without explicitly indicating a refinement. This is the more likely to be useful in practice but, curiously, the equational paradigm has received far more attention from the refinement calculus community. A search-oriented system might be a more natural formalism for work on program synthesis. We leave it as conjecture, for the time being, that an equational refinement calculus is the theory generated from a search calculus.

1.5 Choices

The use of λ^{×→} involves two choices: a typed language and a functional language. By providing an element of syntax-direction, types help in narrowing down the number of refinement rules which can be applied at any stage. As explained above, types can be used to structure specifications.

We choose to use a functional language, simply because the theory is better understood. The lambda calculus is a paradigmatic functional language, and comes in many ‘flavours’. As part of a longer term research plan, we can tackle the problem of finding refinement calculi for complicated computational scenarios in a modular way by first finding a refinement calculus for the simply-typed lambda calculus, and then extending it in a suitable way.

We do not consider full recursion. This may seem like a significant omission, but non-termination would be a significant addition to the calculus and require various choices not central to the basic theory of refinement. Instead, we use well-founded recursion which is sufficiently expressive to get interesting programs. The reasoning involved in constructing fully recursive terminating programs is the same as in constructing programs which use well-founded recursion.

We use classical first-order logic for our program logic. The intention is that we should be able to choose an arbitrary logic, and the corresponding refinement calculus would be induced by the general rules. However, it turns out that in fact, we must choose an extensional logic. That is, a logic for which t and t′ are extensionally equal whenever for all propositions P, P [t/x] ⟺ P [t′/x]. The refinement calculus makes essential use of replacing ‘equals by (extensional) equals’ which would not be possible if our specifications described intensional properties of programs. Intuitionistic logic also has this property; all that matters is that the atomic propositions are extensional. Classical first-order logic is an example of a simple and expressive logic.

As for lambda calculi, extensional logics are better understood than more intensional calculi, so it is more likely that connections can be made between this and other work.

The approach we have taken here is to start with a programming language, a specification language, an equational theory and a notion of satisfaction. It is reasonable to understand the language through its equational theory when we are only concerned with extensional properties. However, an alternative approach would be to start with an operational semantics for the programming language, and understand satisfaction operationally. For example, the equational theory considered here could be generated from a call-by-name operational semantics. The sort of question that might be addressed then would be to find the natural refinement calculus corresponding to a call-by-value semantics, say.

1.6 Related Work

We group relevant work by way of comparison with the calculi of this thesis. We describe related concepts to underdeterminism, work on structured specifications, and some calculi of program development.

1.6.1 Refinement Terms

The study of indefinite descriptions — ‘some x such that P ’ — goes right back to the earliest work on modern logic (e.g. [Ros39]) but the idea there is that a description ranges over semantic values. Hilbert and his collaborators introduced the ϵ-operator (see the monograph [Lei69]), as a formalisation of indefinite descriptions, in order to provide an alternative formulation of mathematical logic. The expression ϵx.P, meaning ‘some x such that P ’, is always defined, and denotes some unknown, but fixed, element which satisfies P, if one exists, and otherwise denotes some arbitrary, but fixed, element.

The logic of ϵ-expressions is modelled using some arbitrary, but fixed, choice function which picks out a member of each nonempty set and returns anything for the empty set. This means, then, that the value of [[x ⊢ ϵy.⊤]](n) is fixed, for any given interpretation of terms, and does not depend on n. Thus, the abstraction λx.ϵy.⊤ denotes a constant function. Thus, the ϵ-operator for indefinite descriptions is essentially a localisation of global variables. Although descriptions can appear embedded anywhere in a term, this is just like using global variables since the denotation must be a value.

We might imagine that a similar technique could be used for our purposes as the terms of our calculus can be thought of a kind of parameterised programs. The simplest choice would be to just consider terms with a free variable in the global context, representing a ‘hole’ to be filled in with program code. This will not work however, since variable capture prevents the variable being replaced by programs which contain local variables. Therefore we must embed underdeterminism locally in the terms with the ?_{τ} construct. We will discuss this further in Chapter 3.

Hermida and Jacobs’ study of indeterminates in the lambda calculus [HJ95] is essentially the same form of global indeterminacy and does not account for substitution allowing variable capture.

Although much work has been published on refinement calculi, there seem to be no fully axiomatised systems. Morgan [Mor94] describes the ‘classical’ refinement calculus, developed independently by Back, Morgan and Morris. This is an imperative language extended with specification constructs. Their system however, uses nondeterminism to express specification constructs. We believe this to be a mistake, as nondeterminism is a computational phenomenon distinct from our view of underdeterminism as a specificational phenomenon at a level above the programming language. Moreover, we might want to consider a combination of nondeterminism and underdeterminism, for example when developing a logic program.

Proof development systems, such as Lego [LP92], allow users to interactively construct a proof by refinement. Intermediate states in a proof development may be modelled as underdetermined terms. The idea there of allowing existential variables in terms is similar to our refinement terms. In a similar vein, so-called logical variables have been used in artificial intelligence, and are essentially the same concept.

We make a more extensive comparison with the refinement calculi of Back et.al., and with Lego, in Section 5.4.

The concept of underdeterminism also arises in linguistics (with the name ‘underspecification’), where semantically ambiguous statements such as “every student is ?_{noun phrase}” are studied. Bos [Bos95] for example, considers a language with metavariables for representing such statements.

1.6.2 Refinement Types

A number of authors have advocated program analysis using annotated type systems. An example in the ‘non-standard type system as program logic’ paradigm is [NN88], a system for binding time analysis (and optimisation). Jensen [Jen91] performs strictness analysis using intersection types and primitive types to indicate termination. Burn [Bur92] considers a more general framework, with intersection and union types. Each of these systems axiomatises property deductions using refinements ϕ ⊑ ϕ′.

Pfenning, who introduced the term “refinement type”, gave a refinement type system for expressing properties of mini-ML programs [FP91]. In another work [Pfe93] he gave an extension to LF with (possibly intensional) properties such as “in normal form” (a property of derivations), given as refinement types. He does not allow refinement types in abstractions though. In both works, the idea is that refinement types offer greater expressivity but carefully restricted to retain desirable properties.

The paper of Coppo, Damiani and Giannini [CDG96] is quite similar to our approach, using refinement types for dead code elimination. They also give a semantics using pers.

There have been various approaches by type theorists to combining logic and types. Feferman’s system of variable types [Fef85] extends λ^{×→} with subset types, though equality does not depend on the type. Refinement (of refinement types) can be defined in the logic, but is not explicitly axiomatised. Talcott [Tal90] used a similar form of refinement types (though the underlying theory is untyped), based on Feferman’s work, in order to express local information for use in transformations to introduce continuations. However, since she lacks a typed equality, local assumptions cannot be discharged and conclusions take the form “t = t′ if x satisfies ϕ”, rather than λx.t =_{ϕ→ψ} λx.t′, say.

Other type-theoretic approaches include [Asp95, AC96], which differ from the present work in being concerned with subtyping type families. Dependency there is at the level of types themselves, whereas we only allow dependent structure at the refinement type level. Aspinall’s [Asp95] dependent type theory, λ_{≤{}}, is formally similar in that it has subtyping on dependent functions and products. Dependency in λ_{≤{}} comes from singleton types, which are a special case of subset types. The purpose of Aspinall’s system is not to be a specification language, however, but to give a type structure to specification building operations. His system is based on subtyping rather than refinement types. We discuss Aspinall’s thesis further in 5.4.2.

Hayashi’s ATTT [Hay94b] is a rich type theory conservatively extending the polymorphic lambda calculus with singleton, union and intersection types. It is based on the refinement type philosophy, maintaining a distinction between types and specified subsets in order to eliminate non-computational information during program extraction. Refinement types are not allowed on abstractions. Dependent function and product types can be defined from the nondependent constructors [Hay94a], as well as subset types constructed using full second order intuitionistic logic. The type theory can also internalise notions of realisability and refinement.

As pointed out by Hayashi, schemas in the Z specification language [Spi92] can be seen as refinement types. They comprise two parts — a typing declaration, and a logical predicate given as a collection of axioms.

The deliverables approach [BM92, McK92] is to consider a program paired with its proof of correctness. We are similarly motivated in wanting to structure specifications using program types, but differ in taking proof existence as more important than the proof itself — terms do not need a witness to satisfy a refinement type. In the conclusion to [McK92], McKinna suggests dropping the requirement for proof existence and, moreover, that implementations should be regarded as being equal up to some extensional equivalence. He proposes a definition of specification which includes an explicit definition of per. Our calculus could be regarded as an internal language for this notion.

The work of Luo [Luo91] presents an encoding of specifications and ‘specification morphisms’ (corresponding to our terms) in an expressive type theory. Our work provides a more direct analysis of the concept of specification by giving an explicit syntax and axiomatisation. The existential form of Martin-Löf’s type theory with subset types in [NPS90] is similar, and indeed, our work on refinement types could be regarded as providing an alternative interpretation of their system.

The program refinement community has traditionally used unstructured specifications of the (x : τ)P form. For example, Morgan [Mor94] describes a refinement calculus based on the use of propositions of first-order predicate logic.

1.6.3 Refinement Calculi

Although there have been many papers on refinement calculi, no authors in this area seem to have presented explicit proof-theoretic axiomatisations of refinement, or given a logic for reasoning about refinement terms. Laws are usually introduced as needed. Proving properties of partially developed programs seems not to have been considered before, except in the sense of regarding abstract programs as specifications so that satisfying a property amounts to refinement.

The classical refinement calculus of Back, Morgan and Morris [Bac88, Mor94, Mor87], based on Dijkstra’s Guarded Command Language [Dij76], is a calculus for deriving imperative programs from specifications expressed in terms of preand postconditions in first-order logic. They do not consider refinement on expressions. The Guarded Command Language is nondeterministic and, though not a refinement calculus, seems to have influenced later refinement calculi in their use of nondeterminism for specification.

Bunkenburg [Bun97] continued their approach for a functional language, retaining some imperative features using a state monad in the style of the computational lambda calculus. Norvell and Hehner [NH92] and Ward [War94] consider functional languages based on the untyped lambda calculus.

All these authors have based their calculi on nondeterminism which, we will see, has consequences for the axiomatisation of refinement.

In the presence of nontermination, this use of nondeterminism gives rise to a choice between demonic and angelic nondeterminism, a choice arising from computational considerations which we believe to be unnecessary. However, we do not consider nontermination here. Most authors have considered total correctness, which leads to the use of demonic nondeterminism. Ward also adds angelic nondeterminism, though it is doubtful whether this brings any advantages when not considering concurrency.

We mentioned the algebraic approach to specification in Section 1.2 above. This is a program specification and development methodology [vL90] centred on the use of abstract data types. An ADT is specified to be an algebra (in the sense of universal algebra) with a given signature, which satisfies a collection of axioms. Thus, a simple specification, ⟨Σ, E⟩, consists of a signature, Σ, and a collection of axioms, E, over that signature. There are various theory-level operators for combining such specifications. In the simplest approach, programs are thought of as total algebras, so there are various extensions to cope with partiality, errors and so on.

There are two styles of semantics. On the one hand, a specification can be viewed as an exact description of a program, so the semantics is defined as some specific algebra (such as the initial or terminal algebra for this specification, with respect to algebra homomorphisms). On the other hand, the specification can be taken to be a description of the required properties of the program, but leaving some possibilities open. In this ‘loose’ approach, the semantics of a specification is taken to be the collection of all algebras which satisfy the specification, or possibly some restriction on this (such as all reachable algebras).

The loose approach is appropriate for program development. Here, refinement is thought of as “the implementation of one specification by another”, and is defined formally as: SP′ is an implementation of SP if sig(SP′) = sig(SP) and mod(SP′) ⊆ mod(SP), that is, as model inclusion over the same signature. This is a very general semantic definition of refinement. There are no axioms for actually proving refinements, for example. More elaborate notions in terms of ‘constructors’ and abstraction have been developed by Sannella and Tarlecki [ST87].

Extended ML [San91, KST97] is a wide-spectrum language which extends (a subset of) the functional programming language ML. It is similar to our calculus in that there are essentially two specification features — a place holder, ?, and the facility to incorporate logical axioms in signatures. See Section 5.4.1 for further discussion.

Bednarczyk and Borzyszkowski [BB95] present a system of rules for finding programs which inhabit specifications. Although they have partial terms representing intermediate steps in the search for inhabitation, they do not have an explicit refinement relation. The Lego proof system [LP92] has a notion of refinement of proof state. If we regard refinement terms as being representations of such a proof state, then Lego’s notion of refinement is like ours.

The relational calculus, Ruby [JS91], is essentially an untyped functional language extended with inverses. It is the use of inverses which gives the language specificational power. Nevertheless, specifications in Ruby are usually functions, and refinement amounts to equational transformation.

1.7 Summary of Thesis

In Chapter 2 we make some preliminary definitions which will be used in the rest of the thesis. We give the basic equational theory of the simply-typed lambda calculus, and describe the applied theory of booleans and naturals. We give a firstorder logic theory over this, explaining how induction is formalised. The calculus and logic can be given a semantics using Henkin models, a class of non-standard set-theoretic models which we define. We prove soundness and completeness of the equational and logical theories with respect to this class of models.

In Chapter 3 we give the calculus of refinement terms. We add ?_{τ} terms to the simply-typed lambda calculus, as a formalisation of true stubs, getting a system for the study of what we call simply-typed underdeterminism. The judgements of the calculus are

Γ ⊢ r : τ
Γ ⊢ r ⊑_{τ} r′

We show that terms can be expressed in a particular canonical form and use this to derive some results about refinement. We then show that all refinements can be given in a standard form in which replacement of stubs by code precedes all equational reasoning.

The terms can be interpreted in Henkin models, with each type σ being ascribed a set σ^{A}, and terms in context Γ ⊢ r : σ interpreted, given the appropriate notion of environment, as subsets of σ^{A}. We show that terms can be expressed in a canonical form, and use this to prove completeness of the calculus with respect to the class of models.

We study a first-order logical theory of refinement, where the atomic propositions are refinements, and give a semantics using Henkin models, for which the logic is proven sound and complete. We infer that the logical theory is conservative over the equational theory.

In fact, since the first-order logic of equality in Chapter 2 is also complete with respect to this class of models, we are able to show that every refinement is equivalent to a proposition in first-order logic involving only equality.

In Chapter 4 we give the calculus of refinement types. This involves an analysis of a suitable notion of specification for refinement, independently of considerations of underdeterminism. We argue that it is natural to use (a syntax representing) partial equivalence relations (pers) for specifications. Terms, then, denote equivalence classes of pers. Refinement typing subsumes typing and formalises the satisfaction of specifications by programs. There is a refinement relation on refinement types.

The judgements are Γ ⊢ t : ϕ

Γ ⊢ P

Γ ⊢ t =_{ϕ} t′

Γ ⊢ ϕ ⊑ ϕ′

The calculus is interpreted using a per structure over the sets in a Henkin model. We prove soundness and completeness with respect to such ‘Henkin pers’, which lets us conclude that the system is a conservative extension of both the simplytyped lambda calculus, and of first-order logic.

In Chapter 5 we present the combination of the two subcalculi, giving a refinement calculus for the refinement of specifications in first-order logic into lambda terms. The syntax of the full refinement calculus is

ϕ ::= 1 | γ | Σ_{x:ϕ}ψ | Π_{x:ϕ}ψ | (x : ϕ)P
r ::= x | k(r_{1}, …, r_{n}) | ∗ | ⟨r, r′⟩ | λx : ϕ.r | ?_{ϕ} | π_{1}(r) | π_{2}(r) |
rr′ | let x : ϕ be r in r′
P ::= ⊥ | P ⊃ P′ | ∀x : ϕ.P | F (r_{1}, …, r_{n}) | r ⊑_{ϕ} r′
Γ ::= ⟨⟩ | Γ, x : ϕ | Γ, P

We show that the subcalculi can be embedded in the full refinement calculus by defining relations between terms which generalise the nonlogical refinement and logical equality of those systems. We show that logical refinement can be factored into these two relations. We also extend the result from Chapter 3 on standardisation of refinements to the full calculus.

Unlike the calculus of simple underdeterminism, refinement terms here can not be interpreted as sets. Both refinement terms and refinement types are interpreted as pers in Henkin Models, and we show how this generalises the semantics of the subcalculi.

We prove soundness of the calculus, but have to be careful with how completeness is formulated. For a restricted class of terms, omitting any higher-order features, we have completeness of the various judgements with respect to interpretation in the class of models.

In Chapter 6 we make some conclusions and suggestions for future work, suggesting how the simple notion of refinement described here could be extended to other situations, and how refinement itself might be incorporated into a larger theory of program development.

1.8 Notation

Following Martin-Löf [Mar96] we refer to the atomic statements of a theory as basic judgements. These are either judgements of well-formedness or of truth. The most general form of judgement is in hypothetico-general form, that is, under the assumption of hypotheses and in a context of free variables. In this thesis we will work in contexts consisting of a combination of variables and propositional assumptions. We will use Γ as a metavariable for contexts, appropriate to whichever calculus is under consideration. We write ⟨⟩ ⊢ B for a judgement in the empty context, or just B.

The metavariable conventions used throughout this thesis are listed in Appendix A. The top-level grammar of the syntactic categories is

Expressions  U ::= r | ϕ | P
Basic Judgements B ::= r : ϕ | P | ϕ : Ref (τ) | P wf | Γ wf
Judgements  J ::= Γ ⊢ B

Preliminaries

In this chapter we give the theoretical basis on which we will build the refinement calculus. We first describe the simply-typed lambda calculus with products and explain how to formulate applied theories. We use a theory of booleans and naturals with primitive recursion as a particular example. We define Henkin interpretations and models, and use them to give a semantics to the lambda calculus. We then describe first-order logic over the equational theory of the simply-typed lambda calculus and show how it can also be modelled using Henkin interpretations, for which we prove completeness theorems.

2.1 Simply-typed Lambda Calculus

We base the theory of refinement on an explicitly typed (Church style) formulation of the simply-typed lambda calculus [Cro93, Mit96]. Here “simply-typed lambda calculus” means with finite products and some axioms over a signature with ground types γ and constants k. The emphasis on axioms will be a feature of this thesis.

2.1.1 Syntax

The terms of an applied lambda theory are with respect to some signature of ground types and constant symbols.

Definition 2.1.1 A type signature consists of a collection of symbols, which we call ground types. We define the simple types over a type signature, G, by the grammar

τ ::= 1 | γ | τ × τ | τ → τ

where γ ∈ G.

For technical reasons, we will assume that all signatures in this thesis are countable, but will not bother to repeat this assumption.

In order to define the notion of constant over a type signature, we follow Mitchell [Mit96] in making a distinction between types and sorts. To each primitive constant we ascribe a sort — a metalevel construct which explains how to form well-formed terms using the constant.

Definition 2.1.2 A sort over type signature, G, with arity n, is a list of n + 1 simple types over G. We write the sort [τ_{1}, …, τ_{n}, τ] using the functional notation τ_{1}, …, τ_{n} → τ (where n ≥ 0).

Definition 2.1.3 Let G be a type signature. We define a constant signature, K, over G, to be a collection of symbols — constants — each of which is ascribed a sort over G.

Definition 2.1.4 A λ^{×→}-signature Sg consists of a type signature, G, and a constant signature, K, over G.

We write k : τ_{1}, …, τ_{n} → τ ∈ K to indicate that constant k in signature Sg = ⟨G, K⟩ has sort τ_{1}, …, τ_{n} → τ. This does not mean that k itself is a welltyped term, but that with n well-formed arguments of the correct types, t_{1} : τ_{1} up to t_{n} : τ_{n}, the term k(t_{1}, …, t_{n}) is well-formed with type τ. For example, the conditional, if then else , has sort bool, τ, τ → τ, but is not itself a well-formed term with type bool × τ × τ → τ.

Alternatively, we could have chosen to give all constants a functional type, but the choice is not significant here. In practice, we will drop the brackets around the arguments to constants and allow ourselves to use any form of mixfix notation.

Rather than write the sort of nullary constants as k :→ τ, we will just write k : τ. Since we will not consider any nullary constants of functional type, there is no ambiguity with unary constants.

Definition 2.1.5 The preterms over signature Sg are given by the grammar

t ::= x | k(t_{1}, …, t_{n}) | ∗ | ⟨t, t′⟩ | λx : τ.t | π_{1}(t) | π_{2}(t) | tt′

For each type we assume a countably infinite number of variables drawn from some set so, strictly speaking, the set of preterms is parameterised on both the signature and the variables. We adopt the convention that in writing x : τ, the variable x is drawn from the set of variables of type τ.

We will use γ and k as metavariables for ground types and constant symbols respectively. Henceforth, we will assume the existence of some signature when writing k and γ.

The contexts are constructed from types and variables via the grammar:

Γ ::= ⟨⟩ | Γ, x : τ

We use the notation Γ, x : τ, Γ′ (with the obvious meaning) to construct contexts, and assume that all the variables in a context are distinct. This assumption of well-formedness is external to the system (or ‘logicistic’). In later calculi we will give explicit rules for the well-formedness of contexts. The ordering of variables is not actually important in the simply-typed lambda calculus, but is significant in the extended calculi of later chapters.

The typing judgement Γ ⊢ t : τ means “under assumption Γ, term t has type τ.” The typing rules of λ^{×→} are standard [Cro93, Mit96] and we do not repeat them here. All judgements are given in a context of variable typings, x_{1} : τ_{1}, …, x_{n} : τ_{n}. We write Sg ▷ Γ ⊢ t : τ when the judgement Γ ⊢ t : τ is derivable from signature Sg, but will drop the Sg ▷ when it is clear which signature is intended.

We will write F V (t) for the set of free variables in preterm t, and use the notation t[t′/x] to indicate the (capture avoiding) substitution of t′ for each free occurrence of variable x in t. The simultaneous substitution of tuple g (or syntactic environment) for the variables in context Γ in t is indicated similarly by t[g/Γ]. We sometimes write t[x] to distinguish all the free occurrences of variable x in t. This does not mean that x is the only free variable, nor that it actually appears free in t. When writing t[x], we may use t[t′] to indicate the substitution t[t′/x].

We adopt the usual notational conventions of the lambda calculus to avoid excessive bracketing: for example, λx : τ.tx means λx : τ.(tx). Round brackets ( ) will sometimes be used to increase readability. We will write ≡ for syntactic equivalence (that is, up to bracketing and α-equivalence), and contrast this with = for the provable equality defined below.

The obvious extensions of these conventions hold for the other syntactic categories introduced later.

Remark 2.1.6 Weakening, permutation and substitution rules are derivable for the typing judgement. This is not the case with the equality judgement, because of the presence of axioms, so we will add rules for these in the next section.

2.1.2 λ^{×→}-Axiom Systems

We give extralogical axioms (on top of the logical axioms of the basic theory) with respect to a particular signature. It is common in the literature to not distinguish between a collection of axioms and the theory they generate, but we will do so here.

Definition 2.1.7 A λ^{×→}-axiom system consists of a λ^{×→}-signature, Sg, and a collection, Ax, of equations in context, Γ ⊢ t =_{τ} t′, well-typed with respect to Sg, that is, Sg ▷ Γ ⊢ t : τ and Sg ▷ Γ ⊢ t′ : τ.

The theorems of λ^{×→} are generated using rules of two kinds — rules for the pure theory of λ-calculus, and rules for inferring theorems from these and the axiom system.

In Figures 2.1 and 2.2 we give the rules for the pure theory of the λ-calculus, and in Figure 2.3 we give the additional rules necessary for inferring theorems from an arbitrary axiom system. It is possible to give a stronger rule of Substitution (Figure 2.3) which would let us derive the congruence rules, but we prefer to give them explicitly thus establishing the pattern for later calculi.

Definition 2.1.8 Let ⟨Sg, Ax⟩ be a λ^{×→}-axiom system. We define the theorems of ⟨Sg, Ax⟩ to be the equations which can be inferred using the rules of Figures 2.1, 2.2 and 2.3. We write ⟨Sg, Ax⟩ ▷ Γ ⊢ t =_{τ} t′ to indicate that equation Γ ⊢ t =_{τ} t′ is a theorem of axiom system ⟨Sg, Ax⟩.

In general, we do not assume that all types are inhabited (by closed terms). Recall here that the (complete) equational theory of the simply-typed lambda calculus differs depending on whether or not empty types are allowed in the semantics [MM91, MMMS87]. We will explicitly state the assumption of inhabitation when necessary, such as when giving a completeness theorem.

2.1.3 Booleans and Naturals

We will use booleans and natural numbers as a running example of an axiom system throughout the thesis. Although we do not consider more complex data types, this could be regarded as a simple case study in how datatypes are treated in refinement calculi. We add booleans and naturals as ground types:

                          γ ::= bool | nat

Reflexivity  {Γ ⊢ t : τ \atop Γ ⊢ t =_{τ} t}

Symmetry  {Γ ⊢ t =_{τ} t′ \atop Γ ⊢ t′ =_{τ} t}

Transitivity  \begin{matrix}Γ ⊢ t =_{τ} t′ & Γ ⊢ t′ =_{τ} t′′ \\ Γ ⊢ t =_{τ} t′′ \end{matrix}

Figure 2.1: Rules for Equational Reasoning

The constants are:

     k ::= 0 | succ | true | false | if  then  else | natrec

In this section we take some time to explain the meaning of the various constants. We use b̄ to signify either of the boolean truth values, and n̄ for numerals. We use b and n as metavariables for expressions of type bool and nat respectively.

In fact, we have a family of recursion operators, natrec_{τ} and so on. Rather than make this explicit or introduce polymorphism we just ignore this (unimportant) point. Similarly, there are separate conditionals for each type τ but we ignore this too.

We give the sortings and equations for booleans and naturals in Figures 2.5 and 2.4. There is a constant for primitive recursion.

                  natrec : τ, (nat → τ → τ), nat → τ

Although we axiomatise constants equationally (as opposed to giving an operational semantics), the idea is that natrec z s n computes a loop in which s is applied n times to z, where s can also use the stage n. For example, a function to add up the first n naturals is sum n = natrec 0 (λx : nat . λy : nat . add x y) n. We will allow ourselves the abuse of language referring to loops and termination.

The primitive recursion natrec z s n will loop at most n times, so is guaranteed to ‘terminate’. This corresponds to for loops in imperative languages.

\begin{matrix}\begin{matrix}Γ, x : σ ⊢ t : τ & Γ ⊢ t′ : σ \\ Γ ⊢ (λx : σ.t)t′ =_{τ} t[t′/x] \end{matrix} & (β) \\ {Γ ⊢ t : σ → τ \atop Γ ⊢ λx : σ.(tx) =_{σ→τ} t} (x ∉ F V (t)) & (η) \end{matrix} Function Equations
\begin{matrix}Γ ⊢ t_{1} : σ_{1} & Γ ⊢ t_{2} : σ_{2} \\ Γ ⊢ π_{1}⟨t_{1}, t_{2}⟩ =_{σ_{1}} t_{1} \end{matrix}  \begin{matrix}Γ ⊢ t_{1} : σ_{1} & Γ ⊢ t_{2} : σ_{2} \\ Γ ⊢ π_{2}⟨t_{1}, t_{2}⟩ =_{σ_{2}} t_{2} \end{matrix}

{Γ ⊢ t : σ × τ \atop Γ ⊢ ⟨π_{1}(t), π_{2}(t)⟩ =_{σ×τ} t}

Product Equations

Unit Equation  {Γ ⊢ t : 1 \atop Γ ⊢ t =_{1} ∗}

{Γ, x : σ ⊢ t =_{τ} t′ \atop Γ ⊢ λx : σ.t =_{σ→τ} λx : σ.t′}

{Γ ⊢ t_{1} =_{τ_{1}} t′_{1} ⋯ Γ ⊢ t_{n} =_{τ_{n}} t′_{n} \atop Γ ⊢ k(t_{1}, …, t_{n}) =_{τ} k(t′_{1}, …, t′_{n})} (k : τ_{1}, …, τ_{n} → τ ∈ K)
\begin{matrix}Γ ⊢ t_{1} =_{σ→τ} t′_{1} & Γ ⊢ t_{2} =_{σ} t′_{2} \\ Γ ⊢ t_{1}t_{2} =_{τ} t′_{1}t′_{2} \end{matrix}  \begin{matrix}Γ ⊢ t_{1} =_{σ} t′_{1} & Γ ⊢ t_{2} =_{τ} t′_{2} \\ Γ ⊢ ⟨t_{1}, t_{2}⟩ =_{σ×τ} ⟨t′_{1}, t′_{2}⟩ \end{matrix}
{Γ ⊢ t =_{σ×τ} t′ \atop Γ ⊢ π_{1}(t) =_{σ} π_{1}(t′)}  {Γ ⊢ t =_{σ×τ} t′ \atop Γ ⊢ π_{2}(t) =_{τ} π_{2}(t′)}

Congruence Equations Figure 2.2: Equality rules

Axioms  Γ ⊢ t =_{τ} t′ (Γ ⊢ t =_{τ} t′ ∈ Ax)

Weakening  {Γ_{1}, Γ_{2} ⊢ t =_{τ} t′ \atop Γ_{1}, x : σ, Γ_{2} ⊢ t =_{τ} t′}

Permutation  {Γ_{1}, x_{1} : σ_{1}, Γ_{2}, x_{2} : σ_{2}, Γ_{3} ⊢ t =_{τ} t′ \atop Γ_{1}, x_{2} : σ_{2}, Γ_{2}, x_{1} : σ_{1}, Γ_{3} ⊢ t =_{τ} t′}

Substitution  \begin{matrix}Γ, x : σ ⊢ t_{1} =_{τ} t′_{1} & Γ ⊢ t_{2} : σ \\ Γ ⊢ t_{1}[t_{2}/x] =_{τ} t′_{1}[t_{2}/x] \end{matrix}

Figure 2.3: Theorems Generated from an Axiom System ⟨Sg, Ax⟩

                         true : bool
                        false : bool
              if  then  else : bool, τ, τ → τ

Sortings

          x : τ, y : τ ⊢ iftruethen x else y =_{τ} x  (β_{true})
         x : τ, y : τ ⊢ iffalsethen x else y =_{τ} y  (β_{false})
f : bool → τ, b : bool ⊢ f(b) =_{τ} if b then f(true) else f(false)  (η)

Axioms

Figure 2.4: Axiom System for Booleans

                              0 : nat
                          succ : nat → nat
                  natrec : τ, (nat → τ → τ), nat → τ

Sortings

z : σ, s : nat → σ → σ ⊢ natrec z s 0 =_{σ} z

z : σ, s : nat → σ → σ, n : nat ⊢ natrec z s (succ n) =_{σ} s n (natrec z s n)

Axioms

Figure 2.5: Axiom System for Naturals

Iteration is a special case of (primitive) recursion, in which the stage number is not used.

natiter z s n = s^{n}(z)

where s^{n} is the n-th composite of s. Formally,

natiter = λz : τ . λs : τ → τ . λn : nat . natrec z (λa : τ.λx : τ.s x) n

In the subsequent chapters we will explain how this treatment of constants is extended to richer calculi, using naturals and booleans as examples.

There are two β-equalities for booleans, and one η-equality. From the ηequality we can deduce

                       if b then t else t =_{σ} t

as well as the commuting conversion

             f(if b then t else t′) = if b then f(t) else f(t′)

2.2 Models of Simply-typed Lambda Calculus

We give interpretations of the calculus in Henkin models. These are a form of ‘non-standard’ set-theoretic model for which simply-typed lambda calculi (in particular, systems containing arithmetic) are complete. As is usual in concrete models of applied lambda calculi, we must consider Henkin models in order to get completeness.

We give a Church style semantics by interpreting typing derivations, and write Γ ⊢ t : τ as a linear shorthand for the derivation of that judgement. (In Chapters 4 and 5 we will interpret ‘pre-judgements’ rather than derivations.)

For a fixed λ^{×→}-signature, we define Henkin interpretations in two stages.

Definition 2.2.1 Let Sg be a λ^{×→}-signature. A Sg-applicative structure (with products) is a tuple of families indexed by the types generated by Sg:

⟨{σ^{A}}, {Proj_{1}^{σ,τ}}, {Proj_{2}^{σ,τ}}, {App^{σ,τ}}, {k^{A}}⟩

To each type σ (not just ground types) we ascribe a set σ^{A}, and to each constant k : τ_{1}, …, τ_{n} → τ, a function k^{A} : τ_{1}^{A} × ⋯ × τ_{n}^{A} → τ^{A}. There are projection and application maps:

Proj_{1}^{σ,τ} : (σ × τ)^{A} → σ^{A}
Proj_{2}^{σ,τ} : (σ × τ)^{A} → τ^{A}
App^{σ,τ} : (σ → τ)^{A} × σ^{A} → τ^{A}

A Henkin interpretation is an applicative structure with two additional conditions, namely, that it is extensional, and that it satisfies the environment model condition.

Definition 2.2.2 An applicative structure with products

⟨{σ^{A}}, {Proj_{1}^{σ,τ}}, {Proj_{2}^{σ,τ}}, {App^{σ,τ}}, {k^{A}}⟩

is extensional when

• 1^{A} has exactly one element

• for all f, f′ ∈ (σ → τ)^{A}, if App^{σ→τ}(f, a) = App^{σ→τ}(f′, a) for all a ∈ σ^{A}, then f = f′

• for all p, p′ ∈ (σ × τ)^{A}, if Proj_{1}^{σ,τ}(p) = Proj_{1}^{σ,τ}(p′) and Proj_{2}^{σ,τ}(p) = Proj_{2}^{σ,τ}(p′) then p = p′

Meanings are given in an environment. For contexts Γ ≡ x_{1} : σ_{1}, …, x_{n} : σ_{n} (n ≥ 0), a Γ-environment in A is a tuple ⟨a_{1}, …, a_{n}⟩ such that a_{i} ∈ σ_{i}^{A} for i = 1, .., n. We will use η to range over environments, and use notation like ⟨η, a, η′⟩ to form the obvious environment. We write η ⊨^{A} Γ to indicate that η is a Γ-environment in interpretation A, but will generally drop the A when it is obvious which interpretation is intended.

Extensionality allows us to use implicit interpretations for abstractions, pairs and the unit which are unique. However, this does not guarantee that there are actually enough elements in the structure to interpret terms at all. The second condition forces models to have enough elements, and is simply given by saying that the interpretation exists. In Figure 2.6 we define the interpretation of terms in an extensional applicative structure. There we write [[Γ ⊢ t : τ]]^{A}(η) for the meaning of the term-in-context Γ ⊢ t : τ with environment η in Henkin interpretation A, and again, usually drop the A. Strictly speaking, for an arbitrary applicative structure this only defines a partial meaning function. To give a semantics, we must assume that it is, in fact, total.

Definition 2.2.3 An applicative structure satisfies the environment model condition when the interpretation in Figure 2.6 in well-defined.

Definition 2.2.4 We say that an applicative structure is a λ^{×→}-Henkin interpretation when it is extensional and satisfies the environment model condition.

We do not have (σ × τ)^{A} = σ^{A} × τ^{A} in general, but we do have a bijection mediated by the projection functions, Proj_{1}^{σ,τ} : (σ × τ)^{A} → σ^{A} and Proj_{2}^{σ,τ} : (σ × τ)^{A} → τ^{A}, and the induced pairing map. In general, (σ → τ)^{A} is embedded in, but not bijective with, σ^{A} → τ^{A}. For a_{1} ∈ σ^{A}, a_{2} ∈ τ^{A}, we write ⟨a_{1}, a_{2}⟩ for the unique element a ∈ (σ × τ)^{A} such that Proj_{1}^{σ,τ}(a) = a_{1} and Proj_{2}^{σ,τ}(a) = a_{2}.

We write Γ ⊨^{A,η} t =_{τ} t′ when [[Γ ⊢ t : τ]]^{A}(η) = [[Γ ⊢ t′ : τ]]^{A}(η), and write Γ ⊨^{A} t =_{τ} t′ when Γ ⊨^{A,η} t =_{τ} t′ for every Γ-environment, η, in A.

Definition 2.2.5 Let A be a Henkin interpretation of λ^{×→}-signature Sg. We say that A is a Henkin model of the axiom system ⟨Sg, Ax⟩ when for each axiom Γ ⊢ t =_{τ} t′ in Ax, Γ ⊨^{A} t =_{τ} t′.

Theorem 2.2.6 (Soundness) Let A be a Henkin model of axiom system ⟨Sg, Ax⟩. If ⟨Sg, Ax⟩ ▷ Γ ⊢ t =_{τ} t′ then Γ ⊨^{A} t =_{τ} t′.

Proof: The proof is by induction on the derivation of the judgement.

As we remarked above, a complete axiomatisation of the lambda calculus depends on whether or not nonemptiness of types is assumed. If types are allowed to be empty then a form of case analysis on emptiness is require, either by having a special rule (as in [MMMS87]) or by using a logic powerful enough to derive this (as in the next section). Alternatively, a wider class of models could be used [MM91]. Here, we will just assume that all types are syntactically inhabited, that is, there are closed terms at every type. In fact, for completeness, a slightly weaker assumption would suffice, namely, that the system is closed under strengthening.

[[Γ, x : σ, Γ′ ⊢ x : σ]]⟨η, a, η′⟩ = a

{[[Γ ⊢ t_{1} : τ_{1}]](η) = a_{1} ⋯ [[Γ ⊢ t_{n} : τ_{n}]](η) = a_{n} \atop [[Γ ⊢ k(t_{1}, …, t_{n}) : τ]](η) = k^{A}(a_{1}, …, a_{n})}

[[Γ ⊢ ∗ : 1]](η) = the unique a in 1^{A}

[[Γ ⊢ t : σ]](η) = a [[Γ ⊢ t′ : τ]](η) = a′

[[Γ ⊢ ⟨t, t′⟩ : σ × τ]](η) = the unique p in (σ × τ)^{A} such that Proj_{1}^{σ,τ}(p) = a and Proj_{2}^{σ,τ}(p) = a′

[[Γ, x : σ ⊢ t : τ]]⟨η, a⟩ = m_{a} for each a in σ^{A}

[[Γ ⊢ λx : σ.t : σ → τ]](η) = the unique f in (σ → τ)^{A} such that ∀a ∈ σ^{A} . App(f, a) = m_{a}

{[[Γ ⊢ t : τ × τ′]](η) = p \atop [[Γ ⊢ π_{1}(t) : τ]](η) = Proj_{1}^{τ,τ′}(p)}

{[[Γ ⊢ t : τ × τ′]](η) = p \atop [[Γ ⊢ π_{2}(t) : τ′]](η) = Proj_{2}^{τ,τ′}(p)}

\begin{matrix}[[Γ ⊢ t : σ → τ]](η) = f & [[Γ ⊢ t′ : σ]](η) = a \\ [[Γ ⊢ tt′ : τ]](η) = App(f, a) \end{matrix}

Figure 2.6: Interpretation of Terms of Simply-typed Lambda Calculus Theorem 2.2.7 (Completeness of equational system) Let ⟨Sg, Ax⟩ be a λ^{×→}- axiom system for which all types are syntactically inhabited. If Γ ⊨^{A} t =_{τ} t′ for all Henkin models A of ⟨Sg, Ax⟩, then ⟨Sg, Ax⟩ ▷ Γ ⊢ t =_{τ} t′.

Proof : We give a sketch of the proof. The idea is to construct a minimal term model T for our signature of ground types, constants and equational assumptions (with no empty types).

1. Fix an infinite context Γ_{∞} with an infinite number of variables at each type. We use Γ_{∞} in judgements to mean some finite Γ′ ⊆ Γ_{∞} so, for example, Γ_{∞} ⊢ t : τ means Γ′ ⊢ t : τ for some Γ′ ⊆ Γ_{∞}.

2. Define τ^{T} as the set of =_{τ}-equivalence classes of open terms of type τ. We write [t] for the equivalence class of t, so τ^{T} = {[t] | Γ_{∞} ⊢ t : τ}. The set τ^{T} is nonempty since we have variables at each type. The projection, application and constant interpretation mappings are interpreted syntactically.

3. We use the fact that Γ_{∞} contains an infinite number of variables at each type to show that T is extensional. If App([t], a) = App([t′], a) for all a ∈ σ^{T}, then since x : σ ∈ Γ_{∞}, we infer that Γ_{∞} ⊢ tx =_{τ} t′x, and so by Congruence Equations and Function Equations (η), we infer that Γ_{∞} ⊢ t =_{σ→τ} t′, i.e. [t] = [t′]. The condition for products is straightforward.

4. Prove that [[Γ ⊢ t : τ]]^{T} (η) = [t[η/Γ]], where t[η/Γ] has the obvious meaning. Hence the environment model condition holds and T is a Henkin interpretation.

5. Now Γ ⊨^{T} t =_{τ} t′ means for all η ⊨^{T} Γ, [[Γ ⊢ t : τ]]^{T} (η) = [[Γ ⊢ t′ : τ]]^{T} (η), that is Γ_{∞} ⊢ t[η/Γ] =_{τ} t′[η/Γ] for all η, (more correctly, ⟨Sg, Ax⟩ ▷ Γ ⊢ t[η/Γ] =_{τ} t′[η/Γ]). Then if Γ ⊢ t =_{τ} t′ ∈ Ax we infer Γ_{∞} ⊢ t[η/Γ] =_{τ} t′[η/Γ], so Γ ⊨_{A,η} t =_{τ} t′ for all η ⊨^{T} Γ and the interpretation T is a Henkin model of ⟨Sg, Ax⟩.

6. Finally, if Γ ⊢ t =_{τ} t′, is an arbitrary equation which is true in the term model, then (for Γ ≡ x_{1} : σ_{1}, …, x_{n} : σ_{n}) setting η = ⟨[x_{1}], …, [x_{n}]⟩ gives Γ_{∞} ⊢ t =_{τ} t′ which, because of inhabitation, implies Γ ⊢ t =_{τ} t′. Hence, we conclude completeness: if an equation is true in all models, it is true in the term model, and so it is provable.

2.3 First-order Logic of Simply-typed Lambda Calculus

We follow the pattern of Section 2.1 and first define the notion of signature, and then axioms over a signature. We now extend signatures with primitive predicate symbols, sometimes called relation symbols.

Definition 2.3.1 A first-order λ^{×→}-signature consists of a λ^{×→}-signature, and a collection of predicate symbols, F, each of which has an arity n, and a sort, given by a list of types, which we write as F : Pred (τ_{1}, …, τ_{n}).

Definition 2.3.2 Let Sg = ⟨G, K, F⟩ be a first-order λ^{×→}-signature. The prepropositions over Sg are:

P ::= ⊥ | F (t_{1}, …, t_{n}) | P ⊃ P′ | ∀x : τ.P | t =_{τ} t′

where F ∈ F, and τ and t are types and preterms over ⟨G, K⟩ respectively.

The atomic propositions are equalities and predications. The other constructors are sufficient to define ⊤, ∧, ∨, ∃ and ¬.

We need a well-formedness judgement

Sg ▷ Γ ⊢ P wf

but omit the rules here. In particular, the proposition t =_{τ} t′ is well-formed when t and t′ have type τ and, for F : Pred (τ_{1}, …, τ_{n}), the predication F (t_{1}, …, t_{n}) is well-formed when t_{i} : τ_{i} for each i. For Δ a list of propositions, we write

Sg ▷ Γ ⊢ Δ wf

when for each P in Δ, Sg ▷ Γ ⊢ P wf.

Definition 2.3.3 A first-order λ^{×→}-axiom system consists of a first-order λ^{×→}- signature, Sg, and a collection, Ax, of closed propositions, well-formed in Sg, that is, Sg ▷ ⟨⟩ ⊢ P wf.

We take axioms in first-order logic to be closed propositions for simplicity’s sake, but this is not important, as quantification gives the same expressiveness as allowing propositions in context. Allowing arbitrary closed propositions as axioms subsumes the equational axioms of λ^{×→}-axiom systems.

We will adopt the convention here, and in subsequent chapters, that when schematic rules are ‘included’ from one calculus to another, the rules should be understood in the latter calculus: that is, metavariables range over expressions in the latter calculus.

In Figures 2.7 and 2.8 we give the rules for classical natural deduction, modified to allow for the possibility of empty types. Only the rules for ⊃, ∀ and ⊥ are necessary, but we give the derived rules for some other connectives as well.

The form of the judgement is Γ; Δ ⊢ P, which means: for all variables in Γ, if each proposition in Δ in true, then P is true.

One property which we want the proof system to have is that all provable judgements are well-formed. It is necessary, therefore, to place well-formedness conditions on those formulae which appear in conclusions but not hypotheses. Otherwise, for example, we could infer that P ⊢ P for any pre-proposition P.

Definition 2.3.4 Let ⟨Sg, Ax⟩ be a first-order λ^{×→}-axiom system. We define the theorems of ⟨Sg, Ax⟩ to be the propositions which can be inferred using the rules of Figures 2.1, 2.2 and 2.3, together with Figures 2.7 and 2.8.

We write ⟨Sg, Ax⟩ ▷ Γ; Δ ⊢ P to indicate that Γ; Δ ⊢ P is a theorem of axiom system ⟨Sg, Ax⟩.

Remark 2.3.5 In this thesis, we will make an important distinction between booleans and propositions. Booleans are terms of a computational datatype and can be evaluated, whereas propositions are the expressions of a logical language. It is common, however, to blur the distinction in program development frameworks, so as to reason about branches of conditionals (using booleans as propositions), and to refine a proposition into a conditional (with a proposition as the condition). However, without making the distinction, it is not clear that the refinement of propositions (as specifications) into booleans is itself a part of program development.

Figure 2.9 gives the first-order axioms for booleans and naturals. These are induction rules, expressed as schemas of closed propositions, but they can also be given as inference rules. We can derive the general rules:

Sg ▷ Γ ⊢ Δ wf Sg ▷ Γ, b : bool ⊢ P wf
Γ; Δ ⊢ P [true/b] ∧ P [false/b] ⊃ ∀b : bool . P
Sg ▷ Γ ⊢ Δ wf Sg ▷ Γ, n : nat ⊢ P wf
Γ; Δ ⊢ P [0/n] ∧ (∀n : nat . P ⊃ P [(succ n)/n]) ⊃ ∀n : nat . P

We retain the equational axioms in Figures 2.4 and 2.5 as part of the first-order axiom systems.

\begin{matrix}Γ; Δ ⊢ P & Γ; Δ ⊢ Q \\ Γ; Δ ⊢ P ∧ Q \end{matrix}

{Γ; Δ ⊢ P ∧ Q \atop Γ; Δ ⊢ P}  {Γ; Δ ⊢ P ∧ Q \atop Γ; Δ ⊢ Q}

Conjunction

{Γ; Δ ⊢ P \atop Γ; Δ ⊢ P ∨ Q}  {Γ; Δ ⊢ Q \atop Γ; Δ ⊢ P ∨ Q}

\begin{matrix}Γ; Δ ⊢ P ∨ Q & Γ, P ⊢ R & Γ, Q ⊢ R \\ Γ; Δ ⊢ R \end{matrix}

Disjunction

{Γ; Δ, P ⊢ Q \atop Γ; Δ ⊢ P ⊃ Q}

\begin{matrix}Γ; Δ ⊢ P & Γ; Δ ⊢ P ⊃ Q \\ Γ; Δ ⊢ Q \end{matrix}

Implication

\begin{matrix}Γ; Δ ⊢ ∀x : τ.P & Γ ⊢ t : τ \\ Γ; Δ ⊢ P [t/x] \end{matrix}

{Γ, x : τ; Δ ⊢ P \atop Γ; Δ ⊢ ∀x : τ.P} (x ∉ Δ)

Universal Quantification

Sg ▷ Γ ⊢ P wf Sg ▷ Γ ⊢ Δ wf

Γ; Δ, ⊥ ⊢ P

{Γ; Δ, ¬P ⊢ ⊥ \atop Γ; Δ ⊢ P}

Falsehood Figure 2.7: Natural Deduction Rules for Classical First-order Logic

Equality  \begin{matrix}Γ; Δ ⊢ t =_{τ} t′ & Γ; Δ ⊢ P [t/x] \\ Γ; Δ ⊢ P [t′/x] \end{matrix}

Sg ▷ Γ ⊢ P wf Sg ▷ Γ ⊢ Δ wf
Γ; Δ, P ⊢ P

Assumptions

Axioms  {Sg ▷ Γ ⊢ Δ wf \atop Γ; Δ ⊢ P} (P ∈ Ax)

Figure 2.8: Natural Deduction Rules cont.

Remark 2.3.6 Although we have defined first-order logic over the simply-typed lambda calculus to include all the rules given in Section 2.1, some of these are derivable. The convention that axioms can be used in arbitrary contexts means that the rules of Figure 2.3 are superfluous. Moreover, the general Equality rule, together with Reflexivity, is enough to derive the other rules in Figure 2.1 and all the Congruence Equations.

The induction rule for booleans says that true and false are the only (closed) booleans. We can prove

                   b : bool ⊢ b = true ∨ b = false

In fact, this also implies the η-equality for booleans.

Using the rule of mathematical induction in Figure 2.9, we can deduce computational induction

P [z] ∀n : nat.∀x : τ . P [x] ⊃ P [s n x]

∀n : nat . P [natrec z s n] \binom{Sg ▷ z : τ, s : nat → τ → τ}{Sg ▷ x : τ ⊢ P [x] wf}

and well-founded induction

{∀n : nat . (∀n′ < n.P [n′]) ⊃ P [n] \atop ∀n : nat . P [n]} (Sg ▷ n : nat ⊢ P [n] wf)

P [true/b] ∧ P [false/b] ⊃ ∀b : bool . P (for Sg ▷ b : bool ⊢ P wf) Booleans

P [0/n] ∧ (∀n : nat . P ⊃ P [(succ n)/n]) ⊃ ∀n : nat . P (for Sg ▷ n : nat ⊢ P wf)

Naturals

Figure 2.9: First-order Logic of Booleans and Naturals

2.4 Models of First-order Logic

We give Henkin models of first-order logic. First we define interpretations for a particular signature.

Definition 2.4.1 Let ⟨G, K, F⟩ be a first-order λ^{×→}-signature. A first-order λ^{×→}-Henkin interpretation, A, of ⟨G, K, F⟩ is a λ^{×→}-Henkin interpretation of ⟨G, K⟩ together with a family of subsets to interpret predicate symbols in F

F^{A} ⊆ τ_{1}^{A} × … × τ_{n}^{A}, for F : Pred (τ_{1}, …, τ_{n})

Figure 2.10 gives the interpretation of propositions in a first-order λ^{×→}-Henkin interpretation. There we interpret well-formed propositions in context, Γ ⊢ P wf, as the set, [[Γ ⊢ P]]^{A} of environments, η ⊨^{A} Γ, in which P holds, though we usually drop the superscript A. We write Γ ⊨^{A,η} P to mean η ∈ [[Γ ⊢ P]]^{A}. If A is a Henkin interpretation, we say that Γ; Δ ⊨^{A,η} P, if for all η ⊨^{A} Γ, if Γ ⊨^{A,η} A for each A in Δ, then Γ ⊨^{A,η} P. We write Γ; Δ ⊨^{A} P when Γ; Δ ⊨^{A,η} P for each η ⊨^{A} Γ.

Definition 2.4.2 Let ⟨Sg, Ax⟩ be a first-order λ^{×→}-axiom system. A Henkin interpretation A of signature Sg is a model of ⟨Sg, Ax⟩ when for each axiom P in Ax, ⊨^{A} P.

One reason for studying the logic is that it is complete over the same class of models as the equational theory. Completeness is with respect to the class of Henkin models (of an axiom system).

In order to prove completeness for an arbitrary axiom system we will construct a term model from its theory. The main problem lies in constructing witnesses for

[[Γ ⊢ ⊥]] = ∅

[[Γ ⊢ F (t_{1}, …, t_{n})]] = {η ⊨ Γ | ⟨[[Γ ⊢ t_{1} : τ_{1}]](η), …, [[Γ ⊢ t_{n} : τ_{n}]](η)⟩ ∈ F^{A}}

[[Γ ⊢ P ⊃ P′]] = {η ⊨ Γ | η ∉ [[Γ ⊢ P]] or η ∈ [[Γ ⊢ P′]] }

[[Γ ⊢ ∀x : τ.P]] = {η ⊨ Γ | for all a in τ^{A} . ⟨η, a⟩ ∈ [[Γ, x : τ ⊢ P]] }

[[Γ ⊢ t =_{τ} t′]] = {η ⊨ Γ | [[Γ ⊢ t : τ]](η) = [[Γ ⊢ t′ : τ]](η)}

Figure 2.10: Interpretation of Well-formed Propositions

existentials. We will achieve this by using the notion of Henkin theory^{1} [vD94]. A Henkin theory, T, has the property that if the proposition ∃x : τ.P is in T, then P [t/x] is in T for some t : τ.

Definition 2.4.3 A first-order λ^{×→}-Henkin theory, T, over an axiom system ⟨Sg, Ax⟩, in context Γ, is a collection of propositions well-formed in Γ, and closed under derivation from ⟨Sg, Ax⟩, such that for every proposition ∃x : τ . P in T, there is a term Γ ⊢ t : τ such that P [t/x] is in T.

We can construct Henkin theories by adding witness variables for existentials, taking care with empty types. As pointed out after Definition 2.1.5 we assume a countably infinite set of variables at each type.

Definition 2.4.4 Let Γ be a context and Δ a set of propositions. We define the Henkin closure of Γ; Δ by the following procedure:

1. First enumerate all the types. Then for each type, τ_{i} (i ≥ 1), we decide if it is to be inhabited or not. For i ≥ 1, we define the proposition Inh_{i} as

{⊤, if Γ; Δ, (Inh_{1} ⊃ ∃x : τ_{1}.⊤), …, (Inh_{i−1} ⊃ ∃x : τ_{i−1}.⊤), ∃x : τ_{i}.⊤ ⊬ ⊥ ⊥, otherwise

2. Then we make a list of all well-formed propositions of the form ∃x : σ_{n}.P_{n} for inhabited σ_{n}, i.e. those for which Inh_{n} = ⊤. This is countable since the signature is countable.

3. Then we make a list of variables {y_{n} : σ_{n}} such that y_{n} ∉ Γ and y_{n} ∉ P_{n′} for n′ ≤ n.

4. We define the Henkin closure of Γ; Δ as Γ_{H}; Δ_{H}, where Γ_{H} = Γ∪{y_{n} : σ_{n}}, and Δ_{H} = Δ ∪ {∃x : σ_{n} . P_{n} ⊃ P_{n}[y_{n}/x]}.

1These have nothing to do with Henkin models.

Although an infinite supply of variables in the context is not necessary to meet the definition of Henkin theory, it is used in the proof below. The point of the completeness proof is to construct the maximal such Henkin theory.

Theorem 2.4.5 (Soundness and Completeness of logical system) Let ⟨Sg, Ax⟩ be a first-order λ^{×→}-axiom system. Then ⟨Sg, Ax⟩ ▷ Γ; Δ ⊢ P iff Γ; Δ ⊨^{A} P for every Henkin model A of ⟨Sg, Ax⟩

Proof: Soundness is straightforward to prove. As for completeness, we do not have a minimal (Henkin) model for the logical system, but nevertheless, we can still use a term model in order to prove deductive completeness, by showing that any consistent theory is satisfiable. For axiom system ⟨Sg, Ax⟩, we want to show that ⟨Sg, Ax⟩ ▷ Γ; Δ ⊢ P iff Γ; Δ ⊨^{A} P in all Henkin models A of ⟨Sg, Ax⟩. (Note that we do not assume that types are nonempty.)

1. Given ⟨Sg, Ax⟩ ▷̸ Γ; Δ ⊢ P we want to find a Henkin model A of ⟨Sg, Ax⟩, and Γ-environment, η, in A such that Γ ⊨^{A,η} A for each A in Δ, ¬P.

2. We construct a maximal consistent theory Δ_{∞} and infinite context Γ_{∞} such that Ax ∪ Δ ∪ {¬P } ⊆ Δ_{∞}, Γ ⊆ Γ_{∞}, and Δ_{∞} is a Henkin theory in Γ_{∞}. First let Γ_{H}; Δ_{H} be the Henkin closure of Γ; Ax ∪ Δ ∪ {¬P }. Now we consider consistent theories in Γ_{H} which extend Δ_{H}. We form the partial order of such theories, ordered by pairwise inclusion. The poset is nonempty since it contains the deductive closure of Δ_{H}. Each member is a Henkin theory. The poset is closed under taking unions, and so each chain has an upper bound. Thus, by Zorn’s Lemma, the collection has a maximal element, Δ_{∞}.

3. We define the term interpretation on equivalence classes of open terms provably equal in Γ_{H}; Δ_{∞}. By renaming variables, we can assume without loss of generality that all terms are open in Γ_{H}. We write [t] for the equivalence class of t, so that [t] = [t′] iff Γ_{H}; Δ_{∞} ⊢ t =_{τ} t′ (i.e. t =_{τ} t′ ∈ Δ_{∞}). First, we show that A is extensional. The interesting case is for function types. We must show that if Γ_{H}; Δ_{∞} ⊢ ta =_{τ} t′a for every Γ_{H} ⊢ a : σ, then Γ_{H}; Δ_{∞} ⊢ t =_{σ→τ} t′. We reason on whether or not ∃x : σ.⊤ is in Δ_{∞}, that is, σ is inhabited.

If it is then, since Γ_{H} contains infinitely many variables for each inhabited type, there exists a variable x : σ ∈ Γ_{H} which does not appear in t or t′. Hence, Γ_{H}; Δ_{∞} ⊢ tx =_{τ} t′x, so Γ_{H}; Δ_{∞} ⊢ λx : σ.tx =_{σ→τ} λx : σ.t′x and Γ_{H}; Δ_{∞} ⊢ t =_{σ→τ} t′. If it is not, then by maximality, ¬∃x : τ.⊤ ∈ Δ_{∞}, and since Δ_{∞} is consistent, there are no terms Γ_{H} ⊢ a : σ and so the implication trivially holds.

4. Prove that [[Γ ⊢ t : τ]]^{A}(η) = [t[η/Γ]], and so A satisfies the environment model condition and is a Henkin interpretation.

5. For all η′ ⊨ Γ′, prove that Γ′ ⊨^{A,η′} Q iff Q[η′/Γ′] ∈ Δ_{∞}, by induction over Q. It is here that the proof rules for each construct are used. We use the fact that for a maximal consistent theory T and well-formed (in Γ_{H}) proposition Q, exactly one of Q, ¬Q is in T. The crucial case is ∃x : τ . Q, which goes through by virtue of Δ_{∞} being a Henkin theory. From this it follows that A is a Henkin model of ⟨Sg, Ax⟩, since for each axiom Q, clearly Q ∈ Δ_{∞}.

6. Finally, using the extension property of Γ_{H}; Δ_{∞}, if Γ ≡ x_{1} : σ_{1}, …, x_{n} : σ_{n}, then we define the Γ-environment, η, to be simply ⟨[x_{1}], …, [x_{n}]⟩, and then Γ ⊨^{A,η} A, for each A in Δ, ¬P.

As a corollary of these completeness results, we can deduce that the first-order calculus is conservative over the equational calculus.

Corollary 2.4.6 Let ⟨Sg, Ax⟩ be a λ^{×→}-axiom system. If all types are inhabited, then ⟨Sg, Ax⟩ ▷ Γ ⊢ t =_{τ} t′ iff ⟨Sg, Ax⟩ ▷ Γ; ⟨⟩ ⊢ t =_{τ} t′.

Proof: Both systems are complete with respect to Henkin models, and the statements have the same interpretation.

Refinement Terms

In this chapter we develop a theory of ‘simple’ refinement. We see that, in addition to aspects of refinement, the use of stubs and skeletons in top-down program development can also be studied in this language. We give a calculus, λ_{?}^{×→} (λ_{?} for short), in which we can express such constructs and a simple semantics using Henkin models for which the calculus is proven sound and complete.^{1}

3.1 Introduction

We introduce an extension of the simply-typed lambda calculus (λ^{×→}) with constructs for expressing a notion we call underdeterminism. Consider the term λx : σ.⟨2, x⟩ of type σ → nat × σ. This term is determined in the sense that we have complete knowledge about it. Suppose now, we know that some term of the same type always returns pairs of which the left component is 2, but we know nothing about the right component. We might write this as λx : σ.⟨2,?_{σ}⟩, where ?_{σ} means ‘some unknown of type σ’ (possibly depending on x). We allow ?_{σ} to stand for any subterm which is well-formed in the local context, so these unknowns can contain the variable x. This is in contrast to the use of variables as indeterminates. If, instead of the stub ?_{σ}, we were to use a free variable y of type σ as a parameter, writing the above term as λx : σ.⟨2, y⟩, then we can substitute any term for y which is well-formed in the global context. The point is, though, that because of variable capture, we cannot substitute x to get λx : σ.⟨2, x⟩. This, of course, is crucial to the use of stubs.

Now, a still less determined term would be λx : σ.⟨?_{nat},?_{σ}⟩. This is a term of type σ → (nat × σ) which returns … what? Evaluation does not make sense in general for such partially constructed terms anyway, but our intuition tells us that the term λx : σ.⟨?_{nat},?_{σ}⟩ carries the same information as λx : σ.?_{nat×σ} and ?_{σ→(nat×σ)}. We would like to prove equivalences such as this, and in general consider a specialisation ordering at each type, such that

1Some of the work of this chapter was presented in [Den97b].

?_{σ→(nat×σ)} ⊑_{σ→(nat×σ)} λx : σ.⟨2,?_{σ}⟩ ⊑_{σ→(nat×σ)} λx : σ.⟨2, x⟩

Moreover, we would like to study how underdeterminism interacts with the usual equational rules of λ^{×→}. Our interest in such a calculus comes from our belief that this is a fundamental aspect of program development. The refinement methodology of program development consists of writing a term meaning ‘a program which satisfies specification ϕ’, and transforming it step by step into an actual program satisfying the specification ϕ. We view types as rudimentary specifications, and defer study of the logic to the next chapter. We believe it is worthwhile to study underdeterminism in isolation from logic, as much of the difficulty in reasoning in refinement calculi is in understanding how underdeterminism interacts with the programming features.

In Section 3.2 we describe the language and its refinement rules. The equational theory is then studied as part of a simple logic. We give a simple denotational semantics in Section 3.4, and show that our calculus is complete for proving refinements valid in this class of models.

3.2 The Calculus

We give the syntax of the language and the classes of judgements. Then we give some syntactic results and a short example of refinement.

3.2.1 Syntax

We start by defining the notion of λ_{?}-signature. In fact, signatures and axioms are the same as we defined in the previous chapter for λ^{×→}. The significance of this is discussed later, in Remark 3.3.4.

Definition 3.2.1 A λ_{?}-signature Sg consists of a collection, G, of ground types (ranged over by γ) and a collection, K, of constant symbols (ranged over by k), each of which is assigned some sort τ_{1}, …, τ_{n} → τ.

We extend the simply-typed lambda calculus with an underdeterminism construct, ?_{τ}, meaning ‘some term with type τ’. We view the type τ as a rudimentary specification and refer to terms with such ‘holes’ for programs to be supplied later as refinement terms. We assume throughout some fixed signature of types and constants.

The types, preterms and contexts are given by:

τ ::= 1 | γ | τ × τ | τ → τ
r ::= x | k(r_{1}, …, r_{n}) | ∗ | ⟨r, r′⟩ | λx : τ.r | ?_{τ} | π_{1}(r) | π_{2}(r) |
rr′ | let x : τ be r in r′
Γ ::= ⟨⟩ | Γ, x : τ

We say that a term is determined if it contains no stubs, that is, subterms of the form ?_{τ}. Otherwise, we call a term underdetermined. We use the metavariable t to range over determined terms, and r over arbitrary underdetermined terms. Note that primitive constants are determined. In fact, each term of the simplytyped lambda calculus is determined. The converse does not hold, however, since we allow let x : σ be t in t′ to be determined. We will see, though, that every determined term is provably equal to a term of the simply-typed lambda calculus.

Intuitively, we can think of a term r as being a kind of description or specification of determined terms, so for example, ⟨2,?_{nat}⟩ is a term which specifies pairs of terms ⟨2, t⟩, where t is any term of type nat. Conversely, we say that ⟨2, t⟩ satisfies ⟨2,?_{nat}⟩.

Formally, r denotes a set of values. Each program, t, to which r can refine, denotes a member of this set. The abstraction λx : τ.r denotes a set of functions, rather than one nondeterministic function. Intuitively, it refines to those abstractions, λx : τ.t, such that for each argument t′ : τ, the result t[t′/x] is a refinement of r[t′/x]. It is not a nondeterministic program which takes an argument t′ and returns a term r[t′/x].

Since let x : σ be t in t′ is provably equal to t′[t/x], we can eliminate all determined let-subterms, and show that every determined term is provably equal to an ordinary term of the simply-typed lambda calculus.

Although terms of the language denote sets of values, we want to regard variables as ranging over single values in order to retain the familiar rules of λ^{×→}. Because of this we will only allow determined terms to be substituted for variables. We use an axiomatisation of let expressions, let x : τ be r in r′, as in the computational lambda calculus [Mog91], as a way of discharging an arbitrary underdetermined term r, without substituting directly for a variable x. The idea is that let x : τ be r in r′ defers the substitution of r for x until r has been refined into some determined t, but still lets us reason about the substitution. The expression let x : τ be r in r′ refines to t′, then, when t′ is a refinement of r′[t/x] for some refinement, t, of r.

Although we do not assume that all types are inhabited (by closed terms), for various statements below we will make this restriction. This avoids the semantic complications mentioned in Chapter 2. Nevertheless, although empty types may or may not be appropriate for any particular programming language, this assumption is independent of the use of the calculus for specification. The reader might assume, though, that this assumption means that our calculus is of no interest in studying program specification, where in traditional type-theoretic approaches, specifications are viewed as possibly empty types. However, the idea in the next chapter is not to use types themselves as specifications, but that a specification (which may be unsatisfiable) is something ‘over’ an ordinary program type. e.g. the specification (n : nat) even(n), of the set of even natural numbers (in the notation of the subsequent chapters) is over nat.

3.2.2 Judgements

We axiomatise an equational theory with two basic judgements

Typing  Γ ⊢ r : τ
Refinement  Γ ⊢ r ⊑_{τ} r′

where Γ is a context of variable assumptions. As is usual with lambda calculi, (in)equations are at a type, which we sometimes drop when not significant. We write the refinement of r to r′ (at τ) as r ⊑_{τ} r′, to indicate that r′ is more determined than r. Note that some authors use ⊒ for refinement.

We take equality to be the derived notion defined as mutual refinement. This is reasonable because, as we show by a semantic argument below, the calculus is a conservative extension of the simply-typed lambda calculus, as determined terms are mutually refinable if and only if they are provably equal in λ^{×→}.

Contexts are well-formed

                              ⊢ Γ wf

if and only if they contain distinct variables. We adopt the convention that in writing a judgement we assume its context to be well-formed.

The typing judgement Γ ⊢ r : τ is axiomatised in Figure 3.1. This just extends the rules of the simply-typed lambda calculus with typing rules for the ? and let constructs. We write Sg▷Γ ⊢ r : τ to indicate that the typing judgement Γ ⊢ r : τ is derivable from signature Sg.

3.2.3 λ_{?}-Axiom Systems

We can define the notion of axiom system with respect to a signature as a set of well-typed equations in context between determined terms.

Variables  Γ, x : σ, Γ′ ⊢ x : σ
\begin{matrix}Γ ⊢ r_{1} : τ_{1} & ⋯ & Γ ⊢ r_{n} : τ_{n} \\ Γ ⊢ k(r_{1}, …, r_{n}) : τ \end{matrix} (k : τ^{1}, …, τ^{n} → τ ∈ K) Constants
Unit  Γ ⊢ ∗ : 1
Stubs  Γ ⊢ ?_{σ} : σ
Product Terms \begin{matrix}Γ ⊢ r : σ & Γ ⊢ r′ : τ \\ Γ ⊢ ⟨r, r′⟩ : σ × τ \end{matrix} ; {Γ ⊢ r : σ × τ \atop Γ ⊢ π_{1}(r) : σ}  {Γ ⊢ r : σ × τ \atop Γ ⊢ π_{2}(r) : τ}
Function Terms  {Γ, x : σ ⊢ r : τ \atop Γ ⊢ λx : σ.r : σ → τ} ; \begin{matrix}Γ ⊢ r : σ → τ & Γ ⊢ r′ : σ \\ Γ ⊢ rr′ : τ \end{matrix}
Γ ⊢ r : τ Γ, x : τ ⊢ r′ : τ′
Γ ⊢ let x : τ be r in r′ : τ′

Let Terms Figure 3.1: Typing Rules Definition 3.2.2 A λ_{?}-axiom system, ⟨Sg, Ax⟩, consists of a λ_{?}-signature, Sg, and a collection, Ax, of equations in context, Γ ⊢ t =_{τ} t′, well-typed with respect to the signature, that is, Sg ▷ Γ ⊢ t : τ and Sg ▷ Γ ⊢ t′ : τ.

We will discuss below (Remark 3.2.4) why we do not allow axioms to be arbitrary refinements. We assume some fixed axiom system ⟨Sg, Ax⟩ throughout.

Definition 3.2.3 Let ⟨Sg, Ax⟩ be a λ_{?}-axiom system. We define the theorems of ⟨Sg, Ax⟩ to be the refinements which can be inferred using the rules of Figures 3.2, 3.3, 3.4, 3.5 and 3.6. We write ⟨Sg, Ax⟩ ▷ Γ ⊢ r ⊑_{τ} r′ to indicate that refinement Γ ⊢ r ⊑_{τ} r′ is a theorem of axiom system ⟨Sg, Ax⟩.

We write the equality, r =_{τ} r′, to mean the mutual refinement, r ⊑_{τ} r′ and r′ ⊑_{τ} r, and extend the rule convention mentioned in the previous chapter, so that when we include rules from λ^{×→} in λ_{?}, equality rules are to be taken as mutual refinements.

Figure 3.2 gives the rules for inferring theorems from a λ_{?}-axiom system. These are the natural extension of the rules in Chapter 2, with the condition that substitution is restricted to determined terms.

The equality rules of Figures 3.3 and 3.4 are on top of those of the simplytyped lambda calculus in Figure 2.2, Chapter 2, which should now be read as mutual refinements.

The rules are given for determined terms. Although we show below various generalisations of these to arbitrary underdetermined terms, we prefer to give the axioms of the calculus in this minimal form as it more clearly shows that refinement is an axiomatisation on top of the underlying equational theory.

Figures 3.3 and 3.4 axiomatise how underdeterminism combines with program constructs via the let expressions. Most of these rules are taken from the computational lambda calculus. The exception is the rule for Abstractions, which makes explicit the ‘hidden dependency’ of specifications on variables in the context. A specification, ?_{τ}, under an abstraction, λx : σ, can be refined to terms which contain the x. We remarked on p. 24 that this is an important difference between ? and ϵ. Now, this is equivalent to specifying some term ?_{σ→τ} outside the λ, which is then applied to x under the λ. This rule is the only addition to Moggi’s computational lambda calculus, and has significant consequences (see Lemma 3.2.10). Logically, we can think of the rule as a form of skolemisation, where abstractions correspond to universal quantifications and let’s to existential quantifications.

Axioms  Γ ⊢ t =_{τ} t′ (Γ ⊢ t =_{τ} t′ ∈ Ax)

Weakening  {Γ_{1}, Γ_{2} ⊢ r ⊑_{τ} r′ \atop Γ_{1}, x : σ, Γ_{2} ⊢ r ⊑_{τ} r′}

Permutation {Γ_{1}, x_{1} : σ_{1}, Γ_{2}, x_{2} : σ_{2}, Γ_{3} ⊢ r ⊑_{τ} r′ \atop Γ_{1}, x_{2} : σ_{2}, Γ_{2}, x_{1} : σ_{1}, Γ_{3} ⊢ r ⊑_{τ} r′}

Substitution  \begin{matrix}Γ, x : σ ⊢ r ⊑_{τ} r′ & Γ ⊢ t : σ \\ Γ ⊢ r[t/x] ⊑_{τ} r′[t/x] \end{matrix}

Figure 3.2: Theorems Generated from a λ_{?}-Axiom System ⟨Sg, Ax⟩

In view of Let Associativity, we use let x_{1} : σ_{1}, x_{2} : σ_{2} be r_{1}, r_{2} in r as an abbreviation of the nested let-term let x_{1} : σ_{1} be r_{1} in (let x_{2} : σ_{2} be r_{2} in r). Note that because of the assumption of well-formedness of contexts, we can omit side-conditions on the occurrence of variables. For example, in Let Associativity, since Γ, y : τ is well-formed, y is not in Γ and so y is not in r′′.

Figures 3.5 and 3.6 axiomatise the refinement relation. The intuition behind the refinement relation is that it should correspond to an increase in information, and a decrease in the possible programs to which a term can refine. There are top-down rules for decomposing a specification by refinement into a combination of simpler ones. We also have a weakening rule Let Weakening which may be thought of as claiming an auxiliary lemma, and a congruence rule for letexpressions, which lets us derive the corresponding rules for pairs, applications and projections.

A number of similar refinement rules for the destructors, and sequent style bottom-up rules for making use of the context are derived below (Propositions 3.2.19 and 3.2.20, respectively).

The equality rules in Figures 2.1 and 2.2, Chapter 2 together with:

Γ ⊢ t : τ Γ, x : τ ⊢ r : τ′
Γ ⊢ let x : τ be t in r[x] =_{τ′} r[t]

Let Beta

Γ ⊢ r : τ
Γ ⊢ let x : τ be r in x =_{τ} r

Let Eta

Γ ⊢ r : τ Γ, y : τ ⊢ r′ : τ′ Γ, x : τ′ ⊢ r′′ : τ′′
Γ ⊢ let x : τ′ be (let y : τ be r in r′[y]) in r′′[x]
=_{τ′′} let y : τ be r in (let x : τ′ be r′[y] in r′′[x])

Let Associativity

Figure 3.3: Equality Rules

Example 3.2.4 Since the definitions of signatures and axiom systems are the same for λ_{?} and λ^{×→}, we just use the axiomatisation of booleans and naturals in Chapter 2, given in Figures 2.4 and 2.5. It is an important point that axioms are given as equations between determined terms, and not as refinements.

The practical implication of this is that if we enrich the language with some new operations, then no extra work is required to give refinement rules, except for equalities (which are a trivial form of refinement). Since all valid refinements are derivable from the rules of the calculus it is unnecessary to have to come up with new rules. The theoretical justification for this will follow from the completeness theorems. For any axiom system, the rules we give are sufficient to prove all true refinements.

We indicate now how this is done in the case of the booleans. We need only use the general rule for combining let-terms and constants. For constant k : τ_{1}, …, τ_{n} → τ, we have

let x_{1} : τ_{1}, …, x_{n} : τ_{n} be r_{1}, …, r_{n} in k(x_{1}, …, x_{n}) = k(r_{1}, …, r_{n})

For booleans this gives for example let x : bool, y : σ, z : σ be r_{1}, r_{2}, r_{3} in (if x then y else z)

                                             =_{σ} if r_{1} then r_{2} else r_{3}

Γ ⊢ r_{1} : τ_{1} ⋯ Γ ⊢ r_{n} : τ_{n} Γ ⊢ let x_{1} : τ_{1}, …, x_{n} : τ_{n} be r_{1}, …, r_{n} in k(x_{1}, …, x_{n}) =_{τ} k(r_{1}, …, r_{n}) (k : τ_{1}, …, τ_{n} → τ ∈ K) Constants
Γ ⊢ r : σ → τ Γ ⊢ r′ : σ

Γ ⊢ let x : σ → τ, x′ : σ be r, r′ in xx′ =_{τ} rr′ Applications

Γ ⊢ r : τ Γ ⊢ r′ : τ′
Γ ⊢ let x : τ, x′ : τ′ be r, r′ in ⟨x, x′⟩ =_{τ×τ′} ⟨r, r′⟩

Pairs

Γ ⊢ r : τ_{1} × τ_{2} Γ ⊢ let x : τ_{1} × τ_{2} be r in π_{i}(x) =_{τ_{i}} π_{i}(r) (i = 1, 2) Projections
Γ, x : σ, y : τ ⊢ r[x, y] : τ′
Γ ⊢ let z : σ → τ be ?_{σ→τ} in λx : σ.r[x, zx]
=_{σ→τ′} λx : σ.(let y : τ be ?_{τ} in r[x, y])

Abstractions Figure 3.4: Equality rules cont.

Variables  Γ, x : σ, Γ′ ⊢ ?_{σ} ⊑_{σ} x
Γ ⊢ ?_{τ} ⊑_{τ} k(?_{τ_{1}}, …,?_{τ_{n}}) (k : τ_{1}, …, τ_{n} → τ ∈ K) Constants
Unit  Γ ⊢ ?_{1} ⊑_{1} ∗
Pairs  Γ ⊢ ?_{σ×τ} ⊑_{σ×τ} ⟨?_{σ},?_{τ}⟩
Abstractions  Γ ⊢ ?_{σ→τ} ⊑_{σ→τ} λx : σ.?_{τ}

Figure 3.5: Refinement Rules

Γ ⊢ r_{1} ⊑_{τ_{1}} r′_{1} Γ, x : τ_{1} ⊢ r_{2} ⊑_{τ_{2}} r′_{2}
Γ ⊢ let x : τ_{1} be r_{1} in r_{2} ⊑_{τ_{2}} let x : τ_{1} be r′_{1} in r′_{2}

(let)

{Γ, x : σ ⊢ r ⊑_{τ} r′ \atop Γ ⊢ λx : σ.r ⊑_{σ→τ} λx : σ.r′}  (abs)

Congruence

Reflexivity  {Γ ⊢ r : τ \atop Γ ⊢ r ⊑_{τ} r}

Transitivity  \begin{matrix}Γ ⊢ r ⊑_{τ} r′ & Γ ⊢ r′ ⊑_{τ} r′′ \\ Γ ⊢ r ⊑_{τ} r′′ \end{matrix}

Γ ⊢ r′ : τ Γ ⊢ r : σ Γ ⊢ r′ ⊑_{τ} let x : σ be r in r′ (x ∉ F V (r′)) Let Weakening

Figure 3.6: Refinement Rules cont.

There is one refinement rule for each constant. If Sg ▷ k : τ_{1}, …, τ_{n} → τ then

Γ ⊢ ?_{τ} ⊑_{τ} k(?_{τ_{1}}, …,?_{τ_{n}})

In combination with the congruence rule for let-terms, we have, in particular then:

                             ?_{bool} ⊑ b̄
                              ?_{nat} ⊑ n̄
                     ?_{τ} ⊑ if ?_{bool} then ?_{τ} else ?_{τ}

Now, we say that a term, r is satisfiable if there exists a determined t, such that r refines to t. Otherwise r is unsatisfiable. A consequence of the way we have axiomatised constants and, in particular, the conditional, is that if empty_{nat} is an unsatisfiable term (see Remark 3.2.18) of type nat, then the conditional if true then 3 else empty_{nat} is unsatisfiable. We can not apply the equation β_{true} of Figure 2.4 since empty_{nat} is not determined.

This is in contrast to refinement calculi based on nondeterminism (e.g.[Bun97, Mor94]) so it is worth considering why we should expect this term to be unsatisfiable.

Although the calculus is (in)equational, the idea is that terms represent stages in the search for a program. We would only expect such a term to have arisen during refinement if the intention is to refine into a conditional, and so both branches must be refined to program code. Since this is not possible, the whole term is unsatisfiable. The fact that the satisfiability of a term depends on the satisfiability of all its subterms means that we can reason about specifications compositionally. In order to implement a specification, we need just implement its components. The alternative would be if we had to do some implementation, combine the resulting specifications somehow, then do some more implementation and so on. Thus we adhere to the ‘principle of modular decomposition’ advocated in [SST92]. The same principle applies when refining into the application of two terms (see Remark 5.2.7).

Remark 3.2.5 Let us consider why a ‘naive’ approach using free variables is not sufficient. Suppose we represent a stage of refinement as a term t[x_{1}, …, x_{n}] with free variables x_{1}, …, x_{n} such that Q_{1}[x_{1}] ∧ ⋯ ∧ Q_{n}[x_{n}] ⊃ P [t[x_{1}, …, x_{n}]]. The free variables stand for unwritten programs and to refine we replace a free variable with a term, possibly introducing more free variables and constraints. We could refine x_{1} to a term t_{1}[y_{1}, …, y_{m}], say, by introducing new free variables, y_{1}, …, y_{m}, with constraints such that

R_{1}[y_{1}] ∧ ⋯ ∧ R_{m}[y_{m}] ⊃ Q_{1}[t_{1}[y_{1}, …, y_{m}]]

However, this does not address the possibility of refining under an abstraction. If t_{1}[y] is of the form λx : σ.t_{2}[y], then the constraint could be given as ∀x : σ.∃y : τ . R[x, y] ⊃ Q_{1}[λx : σ.t_{2}[y]], so now y is not a (global) free variable. If, instead, we represent variables under abstractions using functional variables, and write ∃f : σ → τ.∀x : σ . R[x, fx] ⊃ Q_{1}[λx : σ.t_{2}[fx]], then this just avoids the issue: the unwritten program has the same representation as a variable of type σ → τ and we make no progress!

Logically, this leads to arbitrarily nested quantifiers. In fact, the logic of refinement is a formalism for just that. This justifies the need for a ‘theory of refinement’ which can handle such reasoning more naturally.

Remark 3.2.6 Although a naive use of global variables is unable to account for variable capture, we could some form of variable labelled with the local context.

However, we would then need to make a distinction between variables representing something taken as given, and those representing something which remains to be implemented. We will make some suggestions for such a system, based on logical variables, in Section 6.6.

However, refinement calculi have traditionally been formulated in terms of some kind of specification construct, variations on the stubs we use here. The equivalence with, and axiomatisation in terms of logical variables is a subject for future work.

Remark 3.2.7 The ? is not the same as a nonterminating or undefined term, ⊥. If it was, then in a call-by-value operational semantics, we would have (λx : σ.2)?_{σ} = ?_{nat}, which is not true in λ_{?} if σ is inhabited; in a call-by-name semantics we would have (λx : σ.⟨x, x⟩)?_{σ} = ⟨?_{σ},?_{σ}⟩ and this is not true in λ_{?} if σ has more than one inhabitant.

More significantly, we show below in Remark 3.4.8 that interpreting ? as ⊥ in a cpo does not even provide a sound model of λ_{?}.

Remark 3.2.8 In Chapter 1, we noted that Hilbert’s ϵ-operator differs from ?. In particular, because models are given using a global choice function, the abstraction λx.ϵy.⊤ will always be interpreted as a constant function.

The equational theory of λ_{?} developed in this chapter is based on the idea that we cannot substitute arbitrary underdetermined terms for variables, so we use let-terms. Since ϵ-expressions denote individuals, they can be substituted like other terms. For example, (λx.⟨x, x⟩)ϵx.⊤ = ⟨ϵx.⊤, ϵx.⊤⟩ is sound. In fact, the ϵ-operator can be axiomatised by adding P [t/x] ⊃ P [ϵx.P/x] for each t and P, which is not the case for underdetermined terms.

The logic of the HOL proof assistant contains a (polymorphic) ϵ-operator, ϵ : (α → bool) → α. It is modelled using a choice function, and there is no explicit refinement.

The following lemma provides some insight into underdeterminism. Although stubs can be embedded anywhere in a term, we can give a canonical form with all the underdeterminism moved ‘to the outside’. For example, λx : σ.⟨2,?_{nat}⟩ is equal to let f : σ → nat be ?_{σ→nat} in λx : σ.⟨2, fx⟩. Thus, each term can be viewed as a simple combination of program and specification.

Lemma 3.2.9 For all terms in context Γ ⊢ r : τ, there exists a determined term in context Γ, x_{1} : σ_{1}, …, x_{n} : σ_{n} ⊢ t : τ such that each x_{i} appears exactly once in t, and Γ ⊢ (let x_{1} : σ_{1}, …, x_{n} : σ_{n} be ?_{σ_{1}}, …,?_{σ_{n}} in t) =_{τ} r.

Proof: Use let rules to move the underdeterminism outwards, the important case being the abstractions. Note the linearity – each stub counts exactly once, so ⟨?_{σ},?_{σ}⟩ has canonical form let x : σ, y : σ be ?_{σ},?_{σ} in ⟨x, y⟩ and not let z : σ be ?_{σ} in ⟨z, z⟩ (to which it refines though). We will not, however, use the linearity in appeals to the lemma below. Note also that such canonical forms need not be unique. We sometimes use the useful abbreviation let x_{1} : σ_{1}, …, x_{n} : σ_{n} in t for the canonical form. So, for example, λx : σ.⟨2,?_{nat}⟩ is equal to let f : σ → nat in λx : σ.⟨2, fx⟩. In fact, by repeated pairing, we can always express terms in the simpler form

let x : σ in t.

It is often convenient to write let x_{1} : σ_{1}, …, x_{n} : σ_{n} in t as let Γ in t, where Γ stands for the local context x_{1} : σ_{1}, …, x_{n} : σ_{n}. We also write t[Γ] as informal notation for t[x_{1}, …, x_{n}].

By expressing terms in canonical form, we can prove a few results about let expressions. None of these results hold in the computational lambda calculus.

Lemma 3.2.10 The following rules are admissible:

1. (let-commutativity)

Γ ⊢ r : τ  Γ ⊢ r′ : τ′  Γ, x : τ, y : τ′ ⊢ r′′ : τ′′

Γ ⊢ let x : τ, y : τ′ be r, r′ in r′′ =_{τ′′} let y : τ′, x : τ be r′, r in r′′

2. (let-contraction)

Γ ⊢ r : σ  Γ, x : σ, y : σ ⊢ r′ : τ

Γ ⊢ let x : σ, y : σ be r, r in r′ ⊑_{τ} let z : σ be r in r′[z/x, z/y]

3. (Strengthen local context) For x ∉ F V (r′),

Γ ⊢ r′ : τ′  Γ, x : τ, y : τ′ ⊢ r : τ′′

Γ ⊢ λx : τ.(let y : τ′ be r′ in r) ⊑_{τ→τ′′} let y : τ′ be r′ in λx : τ.r

Proof: We prove (3). First we show that

λx : σ.let y : τ in r[y] ⊑ let y : τ in λx : σ.r[y]  (3.4)

This is:

λx : τ.let y : τ′ in r[y]

= let z : τ → τ′ in λx : τ.r[zx]  (Abstractions)
⊑ let y : τ′ inlet z : τ → τ′ in λx : τ.r[zx]  (Let Weakening)
⊑ let y : τ′ in  (y : τ′ ⊢ ?_{τ→τ′} ⊑ λx : τ.y,
let z : τ → τ′ be λx : τ.y in λx : τ.r[zx]  and Congruence)
= let y : τ′ in λx : τ.r[y]  (Congruence)

Then:

λx : τ.let y : τ′ be r′ in r[x, y]

= λx : τ.let y : τ′ be (let z : σ in t) in r[x, y] (Lemma 3.2.9; can. form)
= λx : τ.let z : σ in (let y : τ′ be t in r[x, y])  (Let Associativity)
⊑ let z : σ in λx : τ.(let y : τ′ be t in r[x, y])  (3.4)
= let z : σ inlet y : τ′ be t in λx : τ.r[x, y]  (Let Beta)
= let y : τ′ be (let z : σ in t) in λx : τ.r[x, y]  (Let Associativity)
= let y : τ′ be r′ in λx : τ.r[x, y]  (can. form)

The proofs of (1) and (2) are carried out similarly, by expressing terms in canonical form.

Commutativity of let’s corresponds to the idea that it does not matter what order we solve subproblems in (so long as they do not depend on each other), and contraction of let’s says that we can solve two identical problems by just solving the problem once and using the solution twice. The third rule illustrates the dependence of underdeterminism on the context. There are more determined terms which satisfy the term on the left, since x can be used in refining r′, but this is not possible when r′ is outside the bound variable on the right.

If all types are inhabited then, in fact, all terms are satisfiable. Because of this, the rule Let Weakening can be strengthened to an equality, i.e. for x ∉ F V (r′), we have r′ = let x : σ be r in r′. We use this fact to derive strengthened forms of the β-equality for products and η-equality for units (Chapter 2) for arbitrary underdetermined terms (of appropriate type). Proposition 3.2.11 If all types are inhabited:

1. \begin{matrix}Γ ⊢ r_{1} : τ_{1} & Γ ⊢ r_{2} : τ_{2} \\ Γ ⊢ π_{i}⟨r_{1}, r_{2}⟩ =_{τ_{i}} r_{i} \end{matrix} (i = 1, 2)
2. {Γ ⊢ r : 1 \atop Γ ⊢ r =_{1} ∗}

Proof:

1. The first is derived as:

π_{1}⟨r_{1}, r_{2}⟩ = π_{1}⟨let x : σ in t_{1}, let y : τ in t_{2}⟩ (for some t_{1}, t_{2})
= let x : σ, y : τ in π_{1}⟨t_{1}, t_{2}⟩
= let x : σ, y : τ in t_{1}
= let x : σ in t_{1}
= r_{1}

The ⊑ of the second last equality is by refining with some determined term of type τ, and the ⊒ is an instance of Let Weakening.

2. Suppose r has type 1. Now r has canonical form let x : σ in t, say, where t has type 1. By Unit Equation we can prove x : σ ⊢ t =_{1} ∗ and so let x : σ in t =_{1} let x : σ in ∗, which since σ is inhabited, equals ∗.

We can strengthen the β-equality for abstractions, without the assumption of nonemptiness.

Proposition 3.2.12 The following is admissible:

\begin{matrix}Γ, x : σ ⊢ r : τ & Γ ⊢ t : σ \\ Γ ⊢ (λx : σ.r)t =_{τ} r[t/x] \end{matrix}

Proof: We use the auxiliary result that for any t of type σ

let f : σ → τ in r[ft/x] = let x : τ in r

Then,

(λx : σ.r)t = (λx : σ.let z : τ′ in t′)t
= let f : σ → τ′ in (λx : σ.t′[fx/z])t
= let f : σ → τ′ in t′[ft/z, t/x]
= let z : τ′ in t′[t/x]
= r[t/x]

Remark 3.2.13 We can use Proposition 3.2.12 to derive Landin’s Equation:

(λx : σ.r)r′ = let x : σ be r′ in r

In principle, therefore, it would be possible to define let in terms of abstraction and application. However, this would lead to unnatural looking equivalents for the let axioms. A more significant reason for making let primitive is that this equation fails when we incorporate logic in the λ_{⊑} calculus in Chapter 5.

Because of Proposition 3.2.12, we can β-reduce applications with arbitrary function bodies. This is significant as it means that underdetermined terms can be executed, up to a point, as ordinary programs. In general, this is not possible as evaluation can not proceed when a stub is encountered. For example, π_{1}(?_{σ×τ}) cannot be reduced. This observation could form the basis for a single-step operational semantics, and we discuss this in Chapter 6.

At this point, we pause to review our motivation for studying this calculus. Program refinement is a stepwise decomposition of logical specifications and their gradual replacement with code. The calculus which we are studying here formalises refinement for a limited form of specification, with no logic, and is a fragment of a larger calculus studied in Chapter 5.

The statement corresponding to Proposition 3.2.12 does not hold in the full system (see Remark 5.2.7) and the auxiliary result fails too. That is, in the presence of logic, we cannot β-reduce with arbitrary function bodies. This is not a problem, as such, since the specification language is not intended to be evaluated. Rather, it is a bonus that β-reduction does make sense here. We believe that this is motivation for studying this subcalculus of a full logical refinement calculus. In an implementation of a program development system, we would like to be able to evaluate partially developed programs such as is formalised in the dynamic semantics of Extended ML [KST97]. If the system is based on the logical refinement calculus of Chapter 5, then we cannot directly evaluate terms. We can, however, use the fragment based on the λ_{?}-calculus.

Now, Propositions 3.2.11 and 3.2.12 show that we can deduce general forms of β-equality, although the axioms for pairs and abstractions are given for determined terms. We do not, however, have η-equalities for arbitrary pairs and abstractions.

To see why this should be so, recall, first, that we think intuitively of λ_{?}-terms as describing a set of values. Equality of terms corresponds to equality of the sets of values. Now, it is possible for two different sets of functions to return the same set of results for each argument. Thus the equation λx : σ.rx =_{σ→τ} r cannot be valid. Likewise, different sets of pairs can have the same set of first (or second) projections.

To illustrate this, we give two terms, r_{1} and r_{2}, which have the same set of results for each argument, but such that r_{1} does not equal r_{2}. Let r_{1} ≡ let b : bool in λx : σ.b and r_{2} ≡ λx : σ.?_{bool}. These terms are different, since one can refine to any function in σ → bool, and the other to any such constant function. We can prove that they have the same set of results, for each argument x : σ, however. In fact, we have λx : σ.r_{1}x =_{σ→τ} r_{2}.

λx : σ.(let b : boolbe ?_{bool} in λx : σ.b)x

= λx : σ.let b : boolbe ?_{bool} in (λx : σ.b)x
= λx : σ.?_{bool}

Remark 3.2.14 As in the computational lambda-calculus, application distributes over let’s. Using the rules of Let Associativity (twice) and Applications we get:

 (let x be r in r′)t = let x_{1} be (let x be r in r′) in x_{1}t
                 = let x be r in (let x_{1} be r′ in x_{1}t)
                 = let x be r in r′t

Although the η-equalities do not hold for arbitrary pairs and abstractions, inequalities are admissible.

Proposition 3.2.15 The following are admissible:

{Γ, x : σ ⊢ r : τ \atop Γ ⊢ λx : σ.rx ⊑_{σ→τ} r}  {Γ ⊢ r : σ × τ \atop Γ ⊢ ⟨π_{1}r, π_{2}r⟩ ⊑_{σ×τ} r}

Proof: We sketch the proof of the first statement. By Lemma 3.2.9, r has canonical form let y : σ′ in t. Then, λx : σ.(let y : σ′ in t)x = λx : σ.let y : σ′ in tx, by the distribution of application over let’s. By Lemma 3.2.10(3), this refines to let y : σ′ in λx : σ.tx, which by Function Equations (η) and Congruence, equals let y : σ′ in t, that is, r.

Proposition 3.2.16 The axiom Abstraction follows from the simpler

Γ, x : σ, y : τ ⊢ t[x, y] : τ′

Γ ⊢ let z : σ → τ in λx : σ.t[x, zx] =_{σ→τ′} λx : σ.(let y : τ in t[x, y]) (3.5)

where the term t is determined.

Proof: The first step is to show, by induction over terms, that (3.5) is sufficient to prove that all terms have a canonical form. For the abstraction case, we have λx : σ.r = λx : σ.let y : τ in t[x, y], by induction, and using (3.5) we get let f : σ → τ in λx : σ.t[x, fx]. Now we prove the full axiom. Assume λx : σ.let y : τ in r : σ → τ′.

λx : σ.let y : τ in r[x, y] = λx : σ.let y : τ, z : τ′ in t[y, z]
= λx : σ.let p : τ × τ′ in t[π_{1}p, π_{2}p]
= let f′′ : σ → τ × τ′ in λx : σ.t[π_{1}(f′′x), π_{2}(f′′x)]
= let f : σ → τ, f′ : σ → τ′ in λx : σ.t[fx, f′x]
= let f : σ → τ in λx : σ.let z : τ′ in t[fx, z]
= let f : σ → τ in λx : σ.r[x, fx]

The next lemma says that our refinement rules are complete, in the sense that they allow us to construct by refinement any program which satisfies a specification (recalling that, for now, we view types as rudimentary specifications).

Lemma 3.2.17 If Γ ⊢ t : σ then Γ ⊢ ?_{σ} ⊑_{σ} t.

Proof: By Proposition 3.2.12, ?_{σ} = (λx : σ.?_{σ})t. Then (λx : σ.?_{σ})t ⊑ (λx : σ.x)t = t.

The fact that this can be proven trivially motivates restricted calculi better suited to proof search, where construction must be on the structure of t. We will discuss this in Chapter 6.

Remark 3.2.18 Using booleans, we can define a form of binary choice on terms. For r, r′ : σ, define

               r[]r′ ≜ let b : boolin (if b then r else r′)

We can prove that [] is commutative, associative, and idempotent, and so is a reasonable notion of choice. This definition is useful because it helps to illustrate the differences between underdeterminism and nondeterminism (e.g.[Dij76]). If the reader is unfamiliar with nondeterminism, then this remark can be safely ignored.

We compare our axiomatisation of underdeterminism with a notion of external nondeterminism, that is, where the nondeterminism arises from the environment making the choice. In particular, we compare a nondeterministic choice operator, +, with []. For example, we would intuitively expect to have λx : nat.2 + 3 = λx : nat.2 + λx : nat.3 (using some constants for naturals). This contrasts with the properties of [] under a binding, since 2[]3 can refine to anything well-formed in the local context. For example, 2[]3 ⊑ if x > 3 then 2 else 3, so λx : nat.2[]3 ⊑ λx : nat.if x > 3 then 2 else 3. Then we have λx : nat.2[]3 ⊑ λx : nat.2[]λx : nat.3, but not the reverse.

Another difference is between nondeterministic failure terms and the analogous idea for underdeterminism — unsatisfiable terms. Suppose the type τ is uninhabited. Then there are unsatisfiable terms at every type. Any term of the form let x : τ in r, where r is any term of type σ, will be unsatisfiable. Now, for nondeterministic failure, we expect r + 0 = r. However, if the term empty_{σ} is unsatisfiable, then r[]empty_{σ} is also unsatisfiable. Informally, ‘r[]empty_{σ} = empty_{σ}’.

We now show that ‘underdeterminism commutes with determinism’, in the sense that underdeterminism at a particular type can be expressed at a lower type using the relevant term constructor. This offers some conceptual justification for regarding underdeterminism as being a feature at a level above a programming language. There is no interaction with computation.

Proposition 3.2.19 The following are derivable:

1. ?_{σ×τ} = ⟨?_{σ},?_{τ}⟩

2. ⟨π_{1}(?_{σ×τ}), π_{2}(?_{σ×τ})⟩ = ?_{σ×τ} 3. If τ is inhabited, then π_{1}(?_{σ×τ}) = ?_{σ}, and if σ is inhabited, then

π_{2}(?_{σ×τ}) = ?_{τ}.

4. ?_{σ→τ} = λx : σ.?_{τ} and ?_{σ→τ}r =_{τ} ?_{τ} for all satisfiable r : σ.

Proof: We prove (1), that ?_{σ×τ} =_{σ×τ} ⟨?_{σ},?_{τ}⟩. Clearly, the refinement rule for Pairs lets us refine from left to right. We prove the other direction.

⟨?_{σ},?_{τ}⟩ = let x : σ, y : τ be ?_{σ},?_{τ} in ⟨x, y⟩
⊑ let z : σ × τ be ?_{σ×τ} in
let x : σ, y : τ be ?_{σ},?_{τ} in ⟨x, y⟩
⊑ let z : σ × τ be ?_{σ×τ} in
let x : σ, y : τ be π_{1}z, π_{2}z in ⟨x, y⟩
= let z : σ × τ be ?_{σ×τ} in ⟨π_{1}z, π_{2}z⟩
= ?_{σ×τ}

The proofs of the other statements are carried out in a similar way, by expressing the terms in canonical form, and using the let-axioms and Lemma 3.2.10 to manipulate the terms.

We can derive some useful refinement rules. As mentioned above, it is possible to derive bottom-up style refinement rules, complementing the top-down rules given as primitive, as well as various congruence rules. Bottom-up rules put together existing programs, whereas top-down rules decompose specifications. We formulate the bottom-up rules in terms of manipulating variables in the context.

Proposition 3.2.20 The following are derivable:

\begin{matrix}\begin{matrix}Γ ⊢ t : σ & Γ, x : τ ⊢ r ⊑_{ν} r′[x] \\ Γ, y : σ → τ ⊢ r ⊑_{ν} r′[yt] \end{matrix} & x ∉ F V (r) \\ {Γ, x : τ ⊢ r ⊑_{σ} r′[x] \atop Γ, z : τ × τ′ ⊢ r ⊑_{σ} r′[π_{1}z]} x ∉ F V (r) & {Γ, y : τ′ ⊢ r ⊑_{σ} r′[y] \atop Γ, z : τ × τ′ ⊢ r ⊑_{σ} r′[π_{2}z]} y ∉ F V (r) \end{matrix} Bottom-up Refinement

\begin{matrix}Γ ⊢ r_{1} ⊑_{σ} r′_{1} & Γ ⊢ r_{2} ⊑_{τ} r′_{2} \\ Γ ⊢ ⟨r_{1}, r_{2}⟩ ⊑_{σ×τ} ⟨r′_{1}, r′_{2}⟩ \end{matrix}  {Γ ⊢ r_{1} ⊑_{τ_{1}} r′_{1} ⋯ Γ ⊢ r_{n} ⊑_{τ_{n}} r′_{n} \atop Γ ⊢ k(r_{1}, …, r_{n}) ⊑_{τ} k(r′_{1}, …, r′_{n})}

{Γ ⊢ r ⊑_{τ_{1}×τ_{2}} r′ \atop Γ ⊢ π_{i}(r) ⊑_{τ_{i}} π_{i}(r′)} (i = 1, 2)  \begin{matrix}Γ ⊢ r_{1} ⊑_{σ→τ} r′_{1} & Γ ⊢ r_{2} ⊑_{σ} r′_{2} \\ Γ ⊢ r_{1}r_{2} ⊑_{τ} r′_{1}r′_{2} \end{matrix}

Congruence Proof: The bottom-up refinement rules are obtained by substitution. The first projection rule, for example, is derived as:

{Γ, x : τ ⊢ r ⊑_{σ} r′[x] \atop Γ ⊢ λx : τ.r ⊑_{τ→σ} λx : τ.r′[x]} ; Γ, z : τ × τ′ ⊢ (λx : τ.r)π_{1}z ⊑_{σ} (λx : τ.r′[x])π_{1}z ; Γ, z : τ × τ′ ⊢ r ⊑_{σ} r′[π_{1}z]

Alternatively, this can be derived directly using Substitution. The congruence rules all follow from the rule for let-terms. For example, the rule for pairs follows since ⟨r_{1}, r_{2}⟩ equals let x_{1} : τ_{1}, x_{2} : τ_{2} be r_{1}, r_{2} in ⟨x_{1}, x_{2}⟩. Then using Congruence (let), this refines to let x_{1} : τ_{1}, x_{2} : τ_{2} be r′_{1}, r′_{2} in ⟨x_{1}, x_{2}⟩, which equals ⟨r′_{1}, r′_{2}⟩. We make a similar point here to that made after Lemma 3.2.17. In a searchdirected refinement calculus (as discussed in Chapter 1), where the rules are given for direct refinement, we would expect these rules would be primitive. Our intention here, though, is to give a system complete for proving arbitrary refinements of the form r ⊑_{τ} r′ and not goal-directed refinements r ⊑_{τ} t.

Another point is that although we can infer forms of the bottom-up rules in which the substitution takes place in both terms (for example, if y : τ′ ⊢ r[y] ⊑_{σ} r′[y] then z : τ × τ′ ⊢ r[π_{2}z] ⊑_{σ} r′[π_{2}z]) the forms we gave are more suitable for directed refinement.

Example 3.2.21 We can use Let Weakening (or the derived rule of contraction in Lemma 3.2.10) to combine two equivalent stubs into one:

⟨?_{σ},?_{σ}⟩ ⊑ let z : σ be ?_{σ} in ⟨?_{σ},?_{σ}⟩ ⊑ let z : σ be ?_{σ} in ⟨z, z⟩

The first step uses Let Weakening, and the second uses the refinement rules Variables, Reflexivity, Pairs and Congruence (let).

Example 3.2.22 We give a short example, using refinement to derive a swap function. Transitivity of refinement means that we can often present much of a refinement derivation as a form of equational reasoning. Here there are two main steps.

?_{σ×τ→τ×σ} ⊑ λz : σ × τ.?_{τ×σ} ⊑ λz : σ × τ.⟨π_{2}z, π_{1}z⟩

Formally, this is

\begin{matrix}⊢ ?_{σ×τ→τ×σ} ⊑ λz : σ × τ.?_{τ×σ} & ⊢ λz : σ × τ.?_{τ×σ} ⊑ λz : σ × τ.⟨π_{2}z, π_{1}z⟩ \\ ⊢ ?_{σ×τ→τ×σ} ⊑ λz : σ × τ.⟨π_{2}z, π_{1}z⟩ \end{matrix} ^{Trans.}

The first hypothesis follows from Congruence (abs), and the second is derived as

\begin{matrix}z : σ × τ ⊢ ?_{τ×σ} ⊑ ⟨?_{τ},?_{σ}⟩ ^{Pairs} & {see below \atop z : σ × τ ⊢ ⟨?_{τ},?_{σ}⟩ ⊑ ⟨π_{2}z, π_{1}z⟩} \\ z : σ × τ ⊢ ?_{τ×σ} ⊑ ⟨π_{2}z, π_{1}z⟩ \end{matrix} ^{Trans.}

⊢ λz : σ × τ.?_{τ×σ} ⊑ λz : σ × τ.⟨π_{2}z, π_{1}z⟩ Congruence

y : τ ⊢ ?_{τ} ⊑ y ^{Variables}
z : σ × τ ⊢ ?_{τ} ⊑ π_{2}z

Congruence

x : σ ⊢ ?_{σ} ⊑ x ^{Variables}
z : σ × τ ⊢ ?_{σ} ⊑ π_{1}z

Congruence

z : σ × τ ⊢ ⟨?_{τ},?_{σ}⟩ ⊑ ⟨π_{2}z, π_{1}z⟩ Congruence

3.3 Metatheory

In this section we prove some results which illustrate the fine structure of the refinement relation. Intuitively, refinement is a combination of coding, where stubs are replaced with program code, and equational reasoning using the rules of the calculus. It is possible to formalise this by defining an explicit coding relation, ⇝, and factorising refinement into a combination of ⇝ and =.

Definition 3.3.1 We define the coding relation on well-typed terms, Γ ⊢ r ⇝ r′, as the reflexive, transitive, congruence closure of the following one-step relation:

{Γ ⊢ t : τ \atop Γ ⊢ ?_{τ} ⇝ t}

We can express any term, r , in the form t[?_{σ_{1}}^{Γ_{1}}, …,?_{σ_{n}}^{Γ_{n}}], where ?_{σ_{i}}^{Γ_{i}} means that the subterm ?_{σ_{i}} appears in ‘local context’ (of let’s and λ’s) Γ_{i}, and each such subterm appears exactly once. Then Γ ⊢ r ⇝ t′ means that for each i = 1 … n, there exists a determined term Γ, Γ_{i} ⊢ t_{i} : σ_{i}, such that Γ ⊢ t[t_{1}, …, t_{n}] =_{τ} t′.

We now show that any refinement to a program can be given as a ‘standard refinement sequence’ consisting of coding followed by equational reasoning.

Lemma 3.3.2 Let ⟨Sg, Ax⟩ be a λ_{?}-axiom system and suppose that ⟨Sg, Ax⟩ ▷ Γ ⊢ r ⊑_{τ} t′. Then there exists a term Sg ▷ Γ ⊢ t : τ such that Γ ⊢ r ⇝ t and ⟨Sg, Ax⟩ ▷ Γ ⊢ t =_{τ} t′.

Proof: We can prove the lemma by induction on the derivation of refinement. The inductive case involves showing that the coding can be extended back along each rule. We consider two cases. The reasoning is similar for the others.

• Let Weakening Suppose Γ ⊢ r ⊑ let x : σ be r′ in r ⇝ t′′, where r ≡ t_{1}[?_{σ_{1}}^{Γ_{1}}, …,?_{σ_{n}}^{Γ_{n}}]. By the inductive hypothesis, there exists a t′ and t_{2}[x] such that Γ ⊢ r′ ⊑ t′ (the details of this refinement do not matter), and Γ, x : σ ⊢ r ⇝ t_{2}[x] with Γ ⊢ let x : σ be t′ in t_{2} = t′′, so Γ ⊢ t_{2}[t′] = t′′. Now the coding of r gives terms Γ, x : σ, Γ_{i} ⊢ u_{i} : σ_{i} for each i = 1 … n, and so Γ, Γ_{i} ⊢ u_{i}[t′/x] : σ_{i}. Hence, Γ ⊢ r ⇝ t_{2}[t′] = t′′, by refining ?_{σ_{i}}^{Γ_{i}} to u_{i}[t_{i}/x].

• Abstractions Since this rule is given as an equation, we must consider the two directions of refinement separately. Suppose Γ ⊢ let z : σ → τ in λx : σ.t′[?_{σ_{i}}^{Γ_{i}}, x, zx]

⊑ λx : σ.(let y : τ in t′[?_{σ_{i}}^{Γ_{i}}, x, y]) ⇝ t

(using the abbreviated notation for let-terms). For clarity, we just indicate the one specification ?_{σ_{i}}. Here Γ_{i} records the context in t′. By induction, there exist terms Γ, x : σ, y : τ, Γ_{i} ⊢ u_{i}[x, y] : σ_{i} and Γ, x : σ ⊢ u[x] : τ such that Γ ⊢ λx : σ.(let y : τ be u in t′[u_{i}, x, y]) = t, that is, Γ ⊢ λx : σ.t′[u_{i}[x, u], x, u] = t. Then, Γ ⊢ λx : σ.u[x] : σ → τ and Γ, z : σ → τ, x : σ, Γ_{i} ⊢ u_{i}[x, zx] : σ_{i} so we can refine the left term to Γ ⊢ let z : σ → τ be λx : σ.u[x] in λx : σ.t′[u_{i}[x, zx], x, zx], which equals λx : σ.t′[u_{i}[x, u[x]], x, u[x]], and this equals t. Now we consider the reverse refinement. Suppose Γ ⊢ λx : σ.(let y : τ in t′[?_{σ_{i}}^{Γ_{i}}, x, y])

⊑ let z : σ → τ in λx : σ.t′[?_{σ_{i}}^{Γ_{i}}, x, zx] ⇝ t

By induction, we have terms Γ ⊢ u : σ → τ and Γ, z : σ → τ, x : σ, Γ_{i} ⊢ u_{i}[z, x] : σ_{i} such that

Γ ⊢ let z : σ → τ be u in λx : σ.t′[u_{i}[z, x], x, zx] = t

so Γ ⊢ λx : σ.t′[u_{i}[u, x], x, ux] = t. Hence we have terms

Γ, x : σ ⊢ ux : τ
Γ, x : σ, y : τ, Γ_{i} ⊢ u_{i}[u, x] : σ_{i}

for which

Γ ⊢ λx : σ.(let y : τ be ux in t′[u_{i}[u, x], x, y]) = t

and so we can refine the left term in the standard way.

We can use this lemma to deduce that all auxiliary ‘claims’ made using Let Weakening can be immediately satisfied.

Lemma 3.3.3 Let ⟨Sg, Ax⟩ be a λ_{?}-axiom system. If ⟨Sg, Ax⟩ ▷ Γ ⊢ let z : σ be r in r′ ⊑_{τ} t′′ then there exists a determined term Sg ▷ Γ ⊢ t : σ such that ⟨Sg, Ax⟩ ▷ Γ ⊢ r ⊑_{σ} t and ⟨Sg, Ax⟩ ▷ Γ ⊢ r′[t/z] ⊑_{τ} t′′.

Proof: Suppose that r ≡ u[?_{σ_{i}}^{Γ_{i}}] and r′ ≡ u′[z,?_{τ_{j}}^{Γ_{j}}]. Then by Lemma 3.3.2, there exist t_{i}, u_{j} such that Γ ⊢ let z : σ be t[t_{i}] in t′[z, u_{j}] =_{τ} t′′. Hence, Γ ⊢ r ⊑_{σ} t[t_{i}] and r′[t[t′]] ⊑_{τ} t′[t[t_{i}], u_{j}] =_{τ} t′′.

Remark 3.3.4 For the above two lemmas to hold it is crucial that the axioms of a λ_{?}-axiom system just consist of determined equations, t =_{τ} t′. If, for example, we had constants k_{1}, k_{2}, k_{3} : τ and axiom k_{1}[]k_{2} ⊑_{τ} k_{3}, that is, let b : bool in (if b then k_{1} else k_{2}) ⊑_{τ} k_{3}, then we would not be able to find a specific t : bool such that if t then k_{1} else k_{2} =_{τ} k_{3}.

In the introduction, we said that refinement should be thought of intuitively as a reduction in the set of programs which satisfy a specification (or refinement term, rather). We can formalise this by defining an ordering r ≲_{τ}^{Γ} r′, on wellformed terms Γ ⊢ r : τ and Γ ⊢ r′ : τ, to mean: for all Γ′ ⊇ Γ, for all determined Γ′ ⊢ t : τ, if Γ′ ⊢ r′ ⊑_{τ} t then Γ′ ⊢ r ⊑_{τ} t.

Lemma 3.3.5 (Refinement Mappings) If let x_{1} : σ_{1}, …, x_{n} : σ_{n} in t ≲_{τ}^{Γ} let y_{1} : τ_{1}, …, y_{m} : τ_{m} in t′ then for all i = 1 … n, there exist terms Γ, y_{1} : τ_{1}, …, y_{m} : τ_{m} ⊢ t_{i} : σ_{i}, such that Γ, y_{1} : τ_{1}, …, y_{m} : τ_{m} ⊢ t[t_{1}/x_{1}, …, t_{n}/x_{n}] =_{τ} t′.

Proof: First of all, note that this can be reduced by repeated pairing to the one variable case. Now if let x : σ in t ≲_{υ}^{Γ} let y : τ in t′, then since Γ, y : τ ⊢ let y : τ in t′ ⊑ t′[y], we have Γ, y : τ ⊢ let x : σ in t ⊑ t′[y], so by Lemma 3.3.3, there must exist a term Γ, y : τ ⊢ u[y] such that Γ, y : τ ⊢ t[u[y]] =_{υ} t′. Because of the definition of ≲_{τ}^{Γ} in terms of all Γ′ ⊇ Γ we do not need to assume that all types are inhabited. If ϵ_{1} and ϵ_{2} are both empty types, then it might seem that let x : ϵ_{1} in ∗ ≲_{1}^{⟨⟩} let x : ϵ_{2} in ∗ since neither term can refine to a determined term in the empty context, yet we cannot produce a term t : ϵ_{2}. The point is, though, that since we can use the context, x : ϵ_{2}, to refine let x : ϵ_{2} in ∗ but not let x : ϵ_{1} in ∗ the terms are not related by ≲_{1}^{⟨⟩}.

These metatheoretic results will be used in the completeness proof of the next section.

3.4 Models

We can interpret the calculus using a simple generalisation of Henkin models. In Chapter 2, Section 2.2, we used λ^{×→}-Henkin Interpretations to give models of the simply-typed lambda calculus. There, terms of type τ were interpreted as elements of a set τ^{A}. We will use the same apparatus, but interpret our underdetermined terms as subsets of the τ^{A}, rather than elements.

We make one additional assumption of our models. We require that the function sets be ‘closed under factoring’.

Definition 3.4.1 We say that a λ^{×→}-Henkin interpretation satisfies the factoring condition if, letting f′ ∈ (τ → τ′)^{A}, f ∈ (σ → τ′)^{A}: if for all b ∈ τ^{A}, there exists an a ∈ σ^{A} such that App(f′, b) = App(f, a), then there exists an element g ∈ (τ → σ)^{A} such that for all b ∈ τ^{A}, App(f′, b) = App(f, App(g, b)). In other words, writing f̄ for the function associated with f ∈ A^{σ→τ}, that is, (a ∈ σ^{A} ↦ App(f, a)): if there exists a function h : τ^{A} → σ^{A} such that f̄′ = h; f̄, then there exists an element g ∈ (τ → σ)^{A} such that f̄′ = ḡ; f̄. Note that we do not require ḡ = h.

The factoring condition is essentially a form of choice axiom and is necessary in order to prove soundness.

Recall that signatures are the same for λ_{?} and λ^{×→}.

Definition 3.4.2 Let ⟨Sg, Ax⟩ be a λ_{?}-signature. A λ_{?}-Henkin interpretation of ⟨Sg, Ax⟩ is a λ^{×→}-Henkin interpretation of ⟨Sg, Ax⟩ with the factoring condition.

Note that we do not require an environment model condition directly on the interpretation of λ_{?} terms, but rather on the underlying λ^{×→}-Henkin interpretation. The condition lifts in the sense that satisfiable terms are given nonempty interpretations. Moreover, the extensionality condition means that programs have unique interpretations (as singleton sets).

Before proceeding further, we give an example of a Henkin interpretation with the factoring condition.

Example 3.4.3 (Full set-theoretic function hierarchy) The sets of the full settheoretic function hierarchy for a given signature are defined inductively as

γ^{A} = any set
1^{A} = {∗}
(σ × τ)^{A} = σ^{A} × τ^{A}
(σ → τ)^{A} = σ^{A} → τ^{A}

The projection and application maps are the usual set-theoretic maps, and constants are interpreted as any elements in the appropriate sets.

We will see below that the open term model is another example. First though, we give an example of a Henkin interpretation which does not satisfy the factoring condition.

Example 3.4.4 The applied λ^{×→} theory with primitive types nat and bool,

and constants 0 : nat, succ : nat → nat, true : bool, false : bool, cond :

bool, nat, nat → nat and eq : nat, nat → bool has a Henkin model in which nat^{A} = N, the set of natural numbers, bool^{A} = B, the set of boolean truth values, and the constants have the expected interpretations, where the function sets are subsets of the space (σ → τ)^{A} ⊆ σ^{A} → τ^{A}. The elements of the model are just those generated by the environment model condition, that is, just those elements required to interpret the terms of the calculus. In particular, there is no element corresponding to the predecessor function, although the function pred such that pred; \overline{succ} = \overline{id_{pos}}, where id_{pos} is the identity on positive naturals λn : nat.cond(eq(n, 0), 1, n), clearly exists.

As for λ^{×→} in Chapter 2, meanings are given in an environment. For context Γ ≡ x_{1} : σ_{1}, …, x_{n} : σ_{n} (n ≥ 0), a Γ-environment, η, in interpretation A, is a tuple of elements defined as for λ^{×→}, and not a tuple of subsets. We write η ⊨^{A} Γ when η is a Γ-environment in A.

Now we interpret Γ ⊢ r : τ inductively on the structure of the typing derivation. In Figure 3.7 we define the interpretation of terms in context, Γ ⊢ r : τ, at Γ-environment, η, written [[Γ ⊢ r : τ]]^{A}(η). We write the λ_{?}-interpretation of ∗ as {∗} rather than (the equivalent) 1^{A} to emphasise the fact that the interpretations are subsets. Similarly, we write the interpretation of ?_{σ} as {a | a ∈ σ^{A}}.

We say that the typing judgement, Γ ⊢ r : τ, is true in interpretation A and Γ-environment, η, written Γ ⊨^{A,η} r : τ, when [[Γ ⊢ r : τ]]^{A}(η) ⊆ τ^{A}. We say that

[[Γ, x : σ, Γ′ ⊢ x : σ]]⟨η, a, η′⟩ = {a}

{[[Γ ⊢ r_{1} : τ_{1}]] = m_{1} ⋯ [[Γ ⊢ r_{n} : τ_{n}]] = m_{n} \atop [[Γ ⊢ k(r_{1}, …, r_{n}) : τ]](η) = \{k^{A}(a_{1}, … a_{n}) | a_{i} ∈ m_{i}(η)\}}

[[Γ ⊢ ∗ : 1]](η) = {∗}

[[Γ ⊢ r : σ]] = m [[Γ ⊢ r′ : τ]] = m′

[[Γ ⊢ ⟨r, r′⟩ : σ × τ]](η) = {a ∈ (σ × τ)^{A} | Proj_{1}^{σ,τ}(a) ∈ m(η), Proj_{2}^{σ,τ}(a) ∈ m′(η)}

{[[Γ, x : σ ⊢ r : τ]] = m \atop [[Γ ⊢ λx : σ.r : σ → τ]](η) = \{f ∈ (σ → τ)^{A} | ∀a ∈ σ^{A} . App(f, a) ∈ m⟨η, a⟩\}}

[[Γ ⊢ ?_{σ} : σ]](η) = {a | a ∈ σ^{A}}

{[[Γ ⊢ r : τ × τ′]] = m \atop [[Γ ⊢ π_{1}(r) : τ]](η) = \{Proj_{1}^{τ,τ′}(a) | a ∈ m(η)\}}

{[[Γ ⊢ r : τ × τ′]] = m \atop [[Γ ⊢ π_{2}(r) : τ′]](η) = \{Proj_{2}^{τ,τ′}(a) | a ∈ m(η)\}}

\begin{matrix}[[Γ ⊢ r : σ → τ]] = m & [[Γ ⊢ r′ : σ]] = m′ \\ [[Γ ⊢ rr′ : τ]](η) = \{App(f, a) | f ∈ m(η), a ∈ m′(η)\} \end{matrix}

[[Γ ⊢ r : σ]] = m [[Γ, x : σ ⊢ r′ : τ]] = m′

[[Γ ⊢ let x : σ be r in r′ : τ]](η) = ⋃_{a∈m(η)} m′(⟨η, a⟩)

Figure 3.7: Interpretation of Well-formed Terms

Γ ⊢ r : τ is true in A, written Γ ⊨^{A} r : τ, when it is true for all Γ-environments in A. It is easily seen that we have soundness of typing, that is, if Γ ⊢ r : τ, then Γ ⊨^{A} r : τ.

Similarly, we say that the refinement, Γ ⊢ r ⊑_{τ} r′, is true in interpretation A and environment η, written Γ ⊨^{A,η} r ⊑_{τ} r′, when [[Γ ⊢ r : τ]]^{A}(η) ⊇ [[Γ ⊢ r′ : τ]]^{A}(η), and define truth in an interpretation to be for all environments. We will usually drop explicit annotation of an interpretation, A.

Definition 3.4.5 Let ⟨Sg, Ax⟩ be a λ_{?}-axiom system, and let A be a λ_{?}-Henkin interpretation of Sg. We say that A is a λ_{?}-Henkin model of ⟨Sg, Ax⟩ when each axiom in Ax is true in A.

We will prove soundness of refinement below, but first, it is easy to see that for determined t, the interpretation [[Γ ⊢ t : τ]](η) is a singleton set. Now we prove a standard lemma.

Lemma 3.4.6 (Substitution Lemma) For well-formed terms in context x_{1} : τ_{1}, …, x_{n} : τ_{n} ⊢ r : τ and Γ ⊢ t_{i} : τ_{i} (i = 1, …, n), we have

[[x_{1} : τ_{1}, …, x_{n} : τ_{n} ⊢ r : τ]](⟨a_{1}, …, a_{n}⟩) = [[Γ ⊢ r[t_{i}/x_{i}]]](η)

where a_{i} is the unique inhabitant of [[Γ ⊢ t_{i} : τ_{i}]](η).

Proof: The proof is a straightforward induction over the typing judgement x_{1} : τ_{1}, …, x_{n} : τ_{n} ⊢ r : τ.

This may be compared with the analogous form for ‘substituting’ an underdetermined term, which follows directly from the semantics of let-terms:

⋃_{a∈m(η)}[[Γ, x : σ ⊢ r′ : τ]](⟨η, a⟩) = [[Γ ⊢ let x : σ be r in r′ : τ]](η)

where m = [[Γ ⊢ r : σ]].

Theorem 3.4.7 (Soundness) Let A be a λ_{?}-Henkin model of λ_{?}-axiom system, ⟨Sg, Ax⟩. If ⟨Sg, Ax⟩ ▷ Γ ⊢ r ⊑_{τ} r′ then Γ ⊨^{A} r ⊑_{τ} r′.

Proof: The proof is by induction on the derivation of the judgement. Most cases are straightforward. We prove two key cases.

• Let Beta The interpretation [[Γ ⊢ let x : τ be t in r : τ′]](η) is defined to be ⋃_{a∈[[Γ⊢t:τ]](η)}[[Γ, x : τ ⊢ r]]⟨η, a⟩. Since t is determined, this is [[Γ, x : τ ⊢ r : τ′]]⟨η, a⟩ where a is the unique member of [[Γ ⊢ t : τ]](η), so by the substitution lemma, the interpretation is [[Γ ⊢ r[t/x] : τ′]](η).

• Abstractions (Equality rules)

It is easier to prove the simpler rule (3.5) of Proposition 3.2.16 sound (implies the soundness of the full rule). Let η ⊨ Γ. We aim to prove that [[Γ ⊢ let z : σ → τ be ?_{σ→τ} in λx : σ.t[x, zx] : σ → τ′]](η)

= [[Γ ⊢ λx : σ.(let y : τ be ?_{τ} in t[x, y]) : σ → τ′]](η)

Now if f ∈ [[Γ ⊢ let z : σ → τ be ?_{σ→τ} in λx : σ.t[x, zx] : σ → τ′]](η), that is, ⋃_{a∈(σ→τ)^{A}}[[Γ, z : σ → τ ⊢ λx : σ.t[x, zx] : σ → τ′]]⟨η, a⟩, then

∃a ∈ (σ → τ)^{A} such that ∀b ∈ σ^{A}, fb ∈ [[Γ, z : σ → τ, x : σ ⊢ t[x, zx] : τ′]]⟨η, a, b⟩ (3.6)

And if f ∈ [[Γ ⊢ λx : σ.(let y : τ be ?_{τ} in t[x, y]) : σ → τ′]](η), then

∀b ∈ σ^{A}, ∃a_{b} ∈ τ^{A} such that fb ∈ [[Γ, x : σ, y : τ ⊢ t[x, y] : τ′]]⟨η, b, a_{b}⟩

(3.7)

We must prove (3.6) and (3.7) are equivalent. Suppose (3.6). Now let b ∈ σ^{A}. We can define a_{b} as App(a, b). We have

{fb ∈ [[Γ, z : σ → τ, x : σ ⊢ t[x, zx] : τ′]]⟨η, a, b⟩ \atop = [[Γ, x : σ, y : τ ⊢ t[x, y] : τ′]]⟨η, b, a_{b}⟩}

by the substitution lemma, so (3.7) holds. Now suppose (3.7). Define h : σ^{A} → (σ × τ)^{A} to be (b ∈ σ^{A} ↦ ⟨b, a_{b}⟩), where a_{b} is any witness of the existential in (3.7). Note that h is a function. We now use the factoring condition to construct a corresponding element of the Henkin model. First define f′ : (σ × τ → τ′)^{A} as the unique inhabitant of [[Γ ⊢ λp : σ × τ.t[π_{1}p, π_{2}p] : σ × τ → τ′]](η). Then we have h; f̄′(b) = f̄′ ⟨b, a_{b}⟩. Now, this equals [[Γ, p : σ × τ ⊢ t[π_{1}p, π_{2}p] : τ′]](η, ⟨b, a_{b}⟩) which, by the substitution lemma, is [[Γ, x : σ, y : τ ⊢ t[x, y]]](⟨η, b, a_{b}⟩). Then (3.7) can be read as f̄ = h; f̄′. By the factoring condition, there exists an element g ∈ (σ → σ × τ)^{A} such that f̄ = ḡ; f̄′. Now define a = App(App(comp, g), p_{2}) ∈ (σ → τ)^{A}, where the elements comp = [[λj : σ → σ × τ . λk : σ × τ → τ . λx : σ . k(jx)]] and p_{2} = [[λp : σ × τ . π_{2}(p)]] exist by the environment model condition. We now prove (3.6). Let b ∈ σ^{A}. Then, by assumption (3.7), fb ∈ [[Γ, x : σ, y : τ ⊢ t[x, y] : τ′]]⟨η, b, a_{b}⟩ which, by the substitution lemma, is [[Γ, z : σ → τ, x : σ ⊢ t[x, zx] : τ′]]⟨η, a, b⟩, so (3.6) holds.

Remark 3.4.8 A naive interpretation of λ_{?} in cpo’s where ? is interpreted as ⊥ is not sound. If we use a cpo with strict functions, then let x : σ in 2 would be interpreted as ⊥_{nat}, and the let-weakening axiom would fail. For example, 2 ⊑ let n : natin 2 would not be sound. On the other hand, if we use non-strict functions, then this conflicts with the fact that the variables of the theory should range over values (i.e. not ⊥) so that the determined equations are extensional. For example, using the eta rule for booleans, we can deduce that ⟨1, 2[]3⟩ = ⟨1, 2⟩[]⟨1, 3⟩. However, b[]b′ would be interpreted as ⊥, so ⟨1, 2[]3⟩ and ⟨1, 2⟩[]⟨1, 3⟩ would be interpreted as ⟨1, ⊥_{nat}⟩ and ⊥_{nat×nat} respectively, and the equation would not be sound.

More significantly, however, we have completeness of the equational theory of simply-typed underdeterminism with respect to the class of Henkin models with factoring. This implies that the system is a conservative extension of the simply-typed lambda calculus.

For the same reasons as in the case of Theorem 2.2.7, we only get a completeness result if we restrict to nonempty types.

Theorem 3.4.9 (Completeness of equational system) Let ⟨Sg, Ax⟩ be a λ_{?}-axiom system for which all types are inhabited. If Γ ⊨^{A} r ⊑_{τ} r′ for all λ_{?}-Henkin models A of ⟨Sg, Ax⟩, then ⟨Sg, Ax⟩ ▷ Γ ⊢ r ⊑_{τ} r′

Proof: We give a sketch of the proof. The idea is to construct a minimal term model for our signature of ground types, constants and equational assumptions (with no empty types).

1. Define the term interpretation T as in Theorem 2.2.7. That is, define an infinite context Γ_{∞} with an infinite number of variables at each type. Define τ^{A} as the set of =_{τ}-equivalence classes of open (with respect to Γ_{∞}) determined terms of type τ, that is, τ^{A} = {[t] | Γ_{∞} ⊢ t : τ}. The projection, application and constant interpretation mappings are interpreted syntactically. Recall from Theorem 2.2.7 that this gives a well-defined Henkin interpretation. To see that the interpretation satisfies the factoring condition, suppose h; f̄ = f̄′, where f = [x : σ ⊢ t[x] : τ′] and f′ = [y : τ ⊢ t′[y] : τ′]. This says that for all terms u′ : τ there exists a term h(u′) : σ such that t[h(u′)] = t′[u′]. In particular then, for the variable y : τ, there exists some term u[y] : σ such that t[u[y]] = t′[y]. We can define η to be λy : τ.u[y], and then ḡ; f̄ = f̄′.

2. Prove that [[Γ ⊢ r : τ]](η) = {[t] | Γ_{∞} ⊢ r[η/Γ] ⊑_{τ} t}, where r[η/Γ] has the obvious meaning. The ?_{σ} case uses Lemma 3.2.17. For abstractions:

[[Γ ⊢ λx : σ.r : σ → τ]](g) = {[u] | for all Γ_{∞} ⊢ t′ : σ , Γ_{∞} ⊢ r[η/Γ, t′/x] ⊑_{τ} ut′}

The result follows since Γ_{∞} ⊢ λx : σ.r ⊑_{σ→τ} λx : σ.t iff for each Γ_{∞} ⊢ t′ : τ, Γ_{∞} ⊢ r[t′/x] ⊑_{τ} t[t′/x]. For let-terms, the interpretation is

[[Γ ⊢ let z : σ be r in r′]](η) = ⋃_{a∈[[Γ⊢r:σ]](η)}[[Γ, z : σ ⊢ r′]]⟨η, a⟩
= ⋃_{a∈{ [t] | r[η/Γ]⊑t}}{[t′] | r′[η/Γ, a/z] ⊑ t′}

Now t′ is in the set when there exists a t such that r[η/Γ] ⊑ t and r′[η/Γ, t/z] ⊑ t′. Hence (let z : σ be r in r′)[η/Γ] ⊑ t′. Conversely, if let z : σ be r[η/Γ] in r′[η/Γ] ⊑ t′, then by Lemma 3.3.3, there exists a t such that r[η/Γ] ⊑ t and r′[η/Γ, t/z] ⊑ t′.

3. Prove that Γ ⊨^{T} r ⊑_{τ} r′ iff ⟨Sg, Ax⟩ ▷ Γ ⊢ r ⊑_{τ} r′. By step 2, Γ ⊨^{T} r ⊑_{τ} r′ is equivalent to: for all η ⊨^{A} Γ, r[η/Γ] ≲_{τ}^{Γ_{∞}} r′[η/Γ]. We show that this, in turn, is equivalent to Γ ⊢ r ⊑_{τ} r′. Clearly Γ ⊢ r ⊑_{τ} r′ ⇒ r[η/Γ] ≲_{τ}^{Γ_{∞}} r′[η/Γ]. To get completeness, we need to prove the converse. The crucial step uses the canonical form of refinement terms. Suppose r[η/Γ] ≲_{τ}^{Γ_{∞}} r′[η/Γ] for all η ⊨^{A} Γ. In particular, then, r ≲_{τ}^{Γ_{∞}} r′. We can express this using canonical forms as

let x_{1} : σ_{1}, …, x_{n} : σ_{n} in t ≲_{τ}^{Γ_{∞}} let y_{1} : τ_{1}, …, y_{m} : τ_{m} in t′

so assume r ≡ let x_{1} : σ_{1}, …, x_{n} : σ_{n} in t and r′ ≡ let y_{1} : τ_{1}, …, y_{m} : τ_{m} in t. Now using Lemma 3.3.5 we deduce the existence of terms Γ_{∞}, y_{1} : τ_{1}, …, y_{m} : τ_{m} ⊢ t_{i} : σ_{i} (for i = 1..n) such that Γ_{∞}, y_{1} : τ_{1}, …, y_{m} : τ_{m} ⊢ t[t_{1}/x_{1}, …, t_{n}/x_{n}] =_{τ} t′, and so, since by Let Weakening, Γ_{∞} ⊢ r ⊑_{τ} let y_{1} : τ_{1}, …, y_{m} : τ_{m} in r, we have Γ_{∞} ⊢ r ⊑_{τ} let y_{1} : τ_{1}, …, y_{m} : τ_{m} in r ⊑_{τ}

let y_{1} : τ_{1}, …, y_{m} : τ_{m} in t[t_{1}/x_{1}, …, t_{n}/x_{n}]

Then this is equal to let y_{1} : τ_{1}, …, y_{m} : τ_{m} in t′, which equals r′. Hence Γ_{∞} ⊢ r ⊑_{τ} r′, and since types are nonempty, we can substitute closed (determined) terms for each variable of Γ_{∞} that is not in Γ, getting Γ ⊢ r ⊑_{τ} r′.

Hence the interpretation T is a model of ⟨Sg, Ax⟩, from which we conclude completeness.

The first two steps are standard in completeness proofs; the third is particular to our calculus.

Corollary 3.4.10 If Γ ⊢ r : σ then Γ ⊢ ?_{σ} ⊑_{σ} r.

Corollary 3.4.11 For axiom systems with inhabited types, λ_{?} is a conservative extension of λ^{×→}.

Proof: Clearly term models for λ^{×→}-axiom systems satisfy the factoring condition and so λ^{×→} is actually complete for λ_{?}-Henkin models. Since both calculi are complete for λ_{?}-Henkin models, and the determined equation Γ ⊢ t =_{τ} t′ has the same interpretation, the result follows.

In fact, conservativity probably holds without the restriction to nonempty types. This could be shown using a more general notion of model, such as Kripke models [MM91].

Remark 3.4.12 An alternative (and probably equivalent) approach would be to interpret the term in context, Γ ⊢ r : τ, as a set of mappings from Γ^{A} to τ^{A}, rather than as a single map taking an environment in Γ^{A} to a subset of τ^{A}. The first approach would avoid the factoring condition.

3.5 First-order Logic of Simply-typed Refinement

Just as we presented a first-order logic over the simply-typed lambda calculus in Section 2.3, now we give a first-order logic of the equational theory of simple refinement.

This combination of logic and refinement is not the same as internalising logic into the stubs themselves; that will be carried out in Chapter 5. In this section we present an ‘external’ logic for reasoning about refinement.

We should regard the logic as being orthogonal to refinement. Again, we use classical first-order logic over a signature of primitive predicate symbols, and constant symbols and ground types. We use first-order λ^{×→}-axiom systems, as in Definition 2.3.3, though now the atomic propositions are predications of the form F (r_{1}, …, r_{n}) and refinements r ⊑_{τ} r′.

In addition to the rules for refinement, we assume some (extralogical) axioms. Given that we have universal quantification, we can take these, without loss of generality, to be closed propositions.

Definition 3.5.1 A first-order λ_{?}-signature is the same as a first-order λ^{×→}- signature.

Definition 3.5.2 Let Sg = ⟨G, K, F⟩ be a first-order λ_{?}-signature. The prepropositions over Sg are:

P ::= ⊥ | F (r_{1}, …, r_{n}) | P ⊃ P′ | ∀x : τ.P | r ⊑_{τ} r′

where F ∈ F, and τ and r are types and λ_{?}-preterms over ⟨G, K⟩ respectively.

The well-formedness judgement

Sg ▷ Γ ⊢ P wf

is the natural extension of that given in Section 2.3. For Δ a list of propositions, we write

Sg ▷ Γ ⊢ Δ wf

when for each P in Δ, Sg ▷ Γ ⊢ P wf.

Definition 3.5.3 A first-order λ_{?}-axiom system consists of a first-order λ_{?}-signature, Sg, and a collection, Ax, of closed λ_{?}-propositions, well-formed in Sg, that is, Sg ▷ ⟨⟩ ⊢ P wf.

We will continue to write equations between determined terms as t =_{τ} t′. Formally, these are propositions of the form t ⊑_{τ} t′ ∧ t′ ⊑_{τ} t. Given our intuition of constructing a refinement theory on top of a λ^{×→}-theory, it might seem natural to restrict axioms to only involve equations. However, if we allow arbitrary propositions, even without refinement, then we will see below that we can encode refinements as propositions of the form ∀x : σ . ∃y : τ.t =_{τ′} t′ anyway.

Definition 3.5.4 Let ⟨Sg, Ax⟩ be a first-order λ_{?}-axiom system. We define the first-order λ_{?}-theorems of ⟨Sg, Ax⟩ to be the judgements which can be inferred using the natural deduction rules of first-order logic in Figures 2.7 and 2.8, Chapter 2 (extended to λ_{?}-propositions), and Figures 3.2 to 3.6, with the convention that Γ ⊢ r ⊑_{τ} r′ means Γ; Δ ⊢ r ⊑_{τ} r′ for all well-formed contexts, in order to include the equational theory of refinement in the logic. As before, the judgements are of the form

Γ; Δ ⊢ P

where Γ is a variable context, Δ is a list of propositions well-formed in Γ, and P a proposition well-formed in Γ. The meaning is: in context Γ, if each proposition in Δ is true, then P is true.

We write ⟨Sg, Ax⟩ ▷ Γ; Δ ⊢ P to indicate that proposition in context Γ ⊢ P is a theorem of axiom system ⟨Sg, Ax⟩.

As in Chapter 2, the logic is complete over the same class of models as the equational theory (now the first-order λ_{?}-Henkin models). After giving a semantics and proving completeness below, we will be able to conclude that refinement can be encoded in the logic using just equality. This does not mean that the notion of refinement is superfluous. Rather, we can consider r ⊑_{τ} r′ to be a useful high-level notation for some Π_{2}-proposition.

Figure 3.8 gives the interpretation of propositions in a first-order λ_{?}-Henkin interpretation. There we interpret well-formed propositions in context, Γ ⊢ P wf,

[[Γ ⊢ ⊥]] = ∅

[[Γ ⊢ F (r_{1}, …, r_{n})]] = {η ⊨ Γ | for all a_{i} in [[Γ ⊢ r_{i}]](η) . ⟨a_{1}, …, a_{n}⟩ ∈ F^{A}}

[[Γ ⊢ P ⊃ P′]] = {η ⊨ Γ | η ∉ [[Γ ⊢ P]] or η ∈ [[Γ ⊢ P′]]}

[[Γ ⊢ ∀x : σ.P]] = {η ⊨ Γ | for all a in σ^{A} . ⟨η, a⟩ ∈ [[Γ, x : σ ⊢ P]]}

[[Γ ⊢ r ⊑_{σ} r′]] = {η ⊨ Γ | [[Γ ⊢ r : σ]](η) ⊇ [[Γ ⊢ r′ : σ]](η)}

Figure 3.8: Interpretation of Well-formed Propositions

as the set, [[Γ ⊢ P]]^{A} of environments, η ⊨^{A} Γ, in which P holds, though we usually drop the superscript A. We write Γ ⊨^{A,η} P to mean η ∈ [[Γ ⊢ P]]^{A}. If A is a Henkin interpretation, we say that Γ; Δ ⊨^{A,η} P, if for all η ⊨^{A} Γ, if Γ ⊨^{A,η} A for each A in Δ, then Γ ⊨^{A,η} P. We write Γ; Δ ⊨^{A} P when Γ; Δ ⊨^{A,η} P for each η ⊨^{A} Γ.

Completeness is with respect to the class of Henkin models (of the axiom system) with the factoring condition.

We extend the definition of Henkin theory to account for refinement. The idea is that a refinement is a form of existential for which we add a witness. If let x : τ in t ⊑_{σ} let x′ : τ′ in t′, then for all x′ : τ, there must exist an x : τ such that t =_{σ} t′. In fact, for completeness we make the stronger assumption that we can make a uniform choice of x for each x′ given by a term t′′ : τ′ → τ.

Definition 3.5.5 A first-order λ_{?}-Henkin theory, T, over axiom system ⟨Sg, Ax⟩, in context, Γ, is a collection of propositions closed under derivation from ⟨Sg, Ax⟩, such that for every proposition ∃x : τ . P, there is a term Γ ⊢ t : τ such that P [t/x] is in T, and for every proposition let x : τ in t ⊑_{σ} let x′ : τ′ in t′ there exists a term Γ ⊢ t′′ : τ′ → τ such that ∀x′ : τ′.t[t′′x′/x] =_{σ} t′ is in T.

One subtle point is that we must be sure that adding witnesses for refinements preserves consistency. This is because the refinement let x : τ in t ⊑_{σ} let x′ : τ′ in t′ is admissibly equivalent to ∃f : τ′ → τ.∀x′ : τ′.t[fx′/x] =_{σ} t′

Theorem 3.5.6 (Soundness and Completeness of logical system) Let ⟨Sg, Ax⟩ be a first-order λ^{×→}-axiom system. Then, ⟨Sg, Ax⟩ ▷ Γ; Δ ⊢ P iff Γ; Δ ⊨^{A} P for all Henkin models A of ⟨Sg, Ax⟩.

Proof: We modify the proof of Theorem 2.4.5. As there, soundness is straightforward to prove, and completeness is shown using a term model. We show that any consistent axiom system is satisfiable. For axiom system ⟨Sg, Ax⟩, we want to show that ⟨Sg, Ax⟩ ▷ Γ; Δ ⊢ P iff Γ; Δ ⊨^{A} P in all λ_{?}-Henkin models, A, of ⟨Sg, Ax⟩. (As in Theorem 2.4.5, we do not assume that types are nonempty.)

1. Given ⟨Sg, Ax⟩ ▷̸ Γ; Δ ⊢ P we want to find a Henkin model A of ⟨Sg, Ax⟩ and Γ-environment, η, in A such that Γ ⊨^{A,η} A for each A in Δ, ¬P.

2. Construct a maximal consistent Henkin theory Δ_{∞} and infinite context Γ_{∞} such that Ax ∪ Δ ∪ {¬P } ⊆ Δ_{∞}, Γ ⊆ Γ_{∞}, and Δ_{∞} is a λ_{?}-Henkin theory in Γ_{∞}. As in the proof of Theorem 2.4.5, we construct a first-order λ_{?}-Henkin theory which extends Γ and Ax ∪ Δ ∪ {¬P }, by taking the Henkin closure of Γ; Ax ∪ Δ ∪ {¬P } and the limit of sets of propositions, Δ′, which are well-formed consistent extensions.

3. Construct the term interpretation A where τ^{A} is the set of equivalence classes of determined terms, where [t] = [t′] iff Γ_{∞}; Δ_{∞} ⊢ t =_{σ} t′ and show that [[Γ ⊢ r : τ]]^{A}(η) = {[t] | Γ_{∞}; Δ_{∞} ⊢ r[η/Γ] ⊑_{τ} t}. This requires the generalisation of Lemma 3.3.2 to logical contexts. In contrast with Remark 3.3.4, this holds because of the construction of the Henkin theory Δ_{∞}. A is a Henkin interpretation with the factoring condition.

4. For all η′ ⊨ Γ′, prove that Γ′ ⊨^{A,η′} P iff P [η′/Γ′] ∈ Δ_{∞}. The crucial cases are ∃x : τ . P and r ⊑_{τ} r′, which go through by virtue of Δ_{∞} being a λ_{?}- Henkin theory. The r ⊑_{τ} r′ case is proven as in Theorem 3.4.9, with the observation that the appropriate generalisation of Lemma 3.3.2 holds.

5. Hence A is a λ_{?}-Henkin model of ⟨Sg, Ax⟩, and for Γ ≡ x_{1} : σ_{1}, …, x_{n} : σ_{n}, defining η to be ⟨[x_{1}], …, [x_{n}]⟩, we have Γ ⊨^{A,η} A, for each A in Δ, ¬P.

The use of canonical forms in the proof of Theorem 3.4.9 suggests that we can translate refinement into first-order logic over λ^{×→}, that is, just using equality of determined terms. If we let r^{∘} denote the canonical form of open term r, then if r^{∘} = let x_{1} : σ_{1}, …, x_{n} : σ_{n} in t, and r^{′∘} = let y_{1} : τ_{1}, …, y_{m} : τ_{m} in t′, define (r ⊑_{υ} r′)^{∘} to be ∀y_{1} : τ_{1}, …, y_{m} : τ_{m} . ∃x_{1} : σ_{1}, …, x_{n} : σ_{n}.t =_{υ} t′.

We use the completeness results (Theorems 3.4.9 and 3.5.6) to infer that the logical system is a conservative extension of the equational system (which, in turn, is a conservative extension of the simply-typed lambda calculus). In fact, we have Corollary 3.5.7 Let ⟨Sg, Ax⟩ be a first-order λ^{×→}-axiom system for which all types are inhabited. Then ⟨Sg, Ax⟩ ▷ Γ ⊢ r ⊑_{τ} r′ iff ⟨Sg, Ax⟩ ▷ Γ; ⟨⟩ ⊢ r ⊑_{τ} r′ iff ⟨Sg, Ax⟩ ▷ Γ; ⟨⟩ ⊢ (r ⊑_{τ} r′)^{∘}.

Proof: Both systems are complete with respect to Henkin models (with the factoring condition), and the statements have the same interpretation.

Remark 3.5.8 The above corollary suggests an alternative proof of completeness. Since (r ⊑ r′)^{∘} is a proposition in first-order logic, we can prove directly that r ⊑ r′ (in λ_{?}) iff (r ⊑ r′)^{∘} (in FOL), and then use the completeness of first-order logic over the λ-calculus (Theorem 2.4.5) to deduce the completeness of first-order λ_{?}.

3.6 Conclusions

Though the language we have presented in this chapter is very simple, we believe that it captures an important part of program refinement. In combination with the calculus of specifications in the next chapter, this gives a calculus which is conceptually simple, but expressive enough to study program development.

Refinement Types

In this chapter we develop a theory of refinement types. This is intended to give us a calculus, λ_{(:)}, for refining specifications, and for proving that programs satisfy specifications. This is a necessary part of a theory of program refinement. We will first justify our view of specification, and then outline what constructs this requires in a calculus and the associated judgement forms. We then give the calculus and illustrate its use with an example verification. In the final section we give a semantics based on Henkin models and prove the system to be sound and complete.^{1}

4.1 Introduction

We address the question of what is a suitable notion of specification for a programming language, where the properties of interest can be expressed using some given program logic. Recall that we restrict our attention to those languages which can be studied using typed lambda calculi, that is, typed functional programming languages.

A number of possibilities can be considered. One is to say that a specification is a type in some expressive type theory. This is the approach taken by [Luo91] and [NPS90] for example. An integer square root function might be specified as the existential type ∃f : nat → nat . Πn : nat . (f n)^{2} = n ∨ (f n)^{2} + 1 = n, where the logic is encoded in the type theory.

The problem is that this only works for an intuitionistic logic. Classical logics are more common for specification, and cannot easily be encoded in type theories. Also, programming languages generally have a simple type system of their own, and this must somehow be related to the specification type theory. A further problem is that it is not easy to combine nontermination with type theories.

1Earlier versions of some of the work in this chapter were presented in [Den97a] and [Den98].

Another possibility is to say that a specification is just a proposition of the program logic with a distinguished free variable. Our square root example would be the proposition ∀n : nat . (f n)^{2} = n ∨ (f n)^{2} + 1 = n, where f is a free variable of type nat → nat. This is the approach traditionally taken by the program refinement community. Morgan [Mor94] describes a refinement calculus based on the use of first-order predicate logic.

However, this approach has a number of shortcomings, which we illustrate with an example below. The main point is that for compositional verification and program development it is better to put more structure on specifications.

In this thesis, we suggest a third possibility: a combination of the program logic with the type theory of the programming language, known as refinement types. The notion of refinement type has been studied extensively in program analysis (under different names) and there are many different systems, depending on the area of interest. The general idea is to have two levels — an underlying level of program types, and a more expressive level of program properties, which are then treated like types. For us, this more expressive level will be the specifications. Hence we can exploit type-theoretic structure in our specifications, but do not need to encode propositions as types but, rather, use them directly.

We describe a verification calculus based on the simply-typed lambda calculus with products (λ^{×→}) and some ground types such as nat and bool. The satisfaction of specifications by programs is axiomatised as a generalised typing relation, in a sense which we make precise below. We do this by viewing specifications as refinements of an underlying type, expressed using the program logic. We use typed classical predicate logic as program logic here, and axiomatise an ordering on the refinement types, to be viewed as an increase in information, or refinement of specifications.

Refinement types are constructed as combinations of types and propositions from the program logic. Types themselves are trivial refinement types, and we can restrict a refinement type to those elements for which some proposition holds. This is similar to subset types [NPS90], though not quite the same since we maintain a distinction from the types themselves. Also, it is convenient to form dependent functions and products at the level of refinement types, even though the underlying types are not dependent.

Refinement types are not the same as subtypes though. For example, nat might be a subtype of real say, but not a refinement type. Though we are careful to distinguish refinement types from subtypes, equality is ‘stratified’ at different refinement types as in subtyping systems.

Contexts consist of both variable assumptions x : ϕ, and propositions P. These are combined so as to make explicit the mutual dependencies in wellformedness. The dependency arises because we allow refinement types in terms, which in turn can appear in propositions.

We give a simple set-theoretic interpretation of the calculus. The main result of this chapter is soundness and completeness with respect to the resulting class of models.

In Section 4.2 we consider a simple example of specifying and verifying a program in order to motivate the features of our calculus. We then give the syntax and rules of the calculus in Section 4.3. In Section 4.4 we return to the example. Section 4.5 gives the semantics and proofs of soundness and completeness. Finally, we give some conclusions in Section 4.6.

4.2 Example

Let us consider specifying division by 2 on the naturals and verifying that a program satisfies the specification. We will take the simply-typed lambda calculus and classical first-order predicate logic as simple programming and specification languages respectively. We will use the constant, natiter, for iteration over the naturals, where natiter z f n computes the n-th iterate f^{n}(z). As a first approximation to specifications we use propositions with a distinguished free variable, which we write as (x : τ)P where τ is the type of the variable x in proposition P.

A program div2 which implements division on the naturals is

                div2 = λn : nat . π_{1}(div2′ n) : nat → nat

where this uses the auxiliary function

div2′ = natiter ⟨0, 0⟩ (λp : nat × nat . ⟨π_{2}p, π_{1}p + 1⟩)

Now this can be specified as

div2 spec = (f : nat → nat) ∀n : nat . n = 2 ∗ fn ∨ n = 2 ∗ fn + 1

We want to axiomatise a satisfaction relation sat between programs (closed terms) and specifications, so that we can prove

                        div2 sat div2 spec

One simple way of doing this is to say that t sat (x : τ)P is just taken to be a notation for a typing and a proposition, with the rule that t sat (x : τ)P when t : τ and P [t/x]. This example reduces then to proving

∀n : nat.n = 2 ∗ div2(n) ∨ n = 2 ∗ div2(n) + 1

Now, our specification language is rather cumbersome as it stands, so let us introduce dependent products and functions as abbreviations

Σ_{x:σ|P} (y : τ)Q for (z : σ × τ)P [π_{1}z/x] ∧ Q[π_{1}z/x, π_{2}z/y]
Π_{x:σ|P} (y : τ)Q for (f : σ → τ)∀x : τ . P ⊃ Q[fx/y]

The dependent function Π_{x:σ|P} (y : τ)Q specifies some function which for all x : σ such that P, returns a y : τ such that Q. This has combined the two quantifications in (f : σ → τ) ∀x : σ . P ⊃ Q[fx/y], which we read as some f : σ → τ such that for all x : σ, if P then Q[fx/y]. If we allow ourself the further abbreviation of viewing types as trivial specifications, so that for example, nat can stand for x : nat|⊤, then we can write our specification more compactly as

div2 spec = Π_{n:nat}(m : nat)n = 2 ∗ m ∨ n = 2 ∗ m + 1

Now, using our abbreviations, the following rule is admissible from our definition

of sat

n : nat ⊢ π_{1}(div2′ n) sat (m : nat)n = 2 ∗ m ∨ n = 2 ∗ m + 1
λn : nat.π_{1}(div2′ n) sat Π_{n:nat}(m : nat)n = 2 ∗ m ∨ n = 2 ∗ m + 1

where we informally understand the sequent n : nat ⊢ t sat ϕ to mean for all closed t′ : nat (or equivalently, for all numerals), t[t′/n] sat ϕ[t′/n]. In general then, we want to consider satisfaction in an arbitrary context. Note the similarity to a typing rule. In fact, not only are Σ and Π useful structuring devices for specifications, they are also useful for proofs, as specifications of programs often tend to be most naturally expressed and proved in a ‘shape’ similar to the program.

For example, the program div2 is an abstraction and the specification div2 spec is of the form Π_{x:ϕ}ψ. The rule directly reflects a natural proof that div2 satisfies div2 spec. Similarly, the auxiliary function div2′ has specification

div2′ spec = Π_{n:nat}Σ_{m:nat|n=2m∨n=2m+1}(m′ : nat)m + m′ = n

The proof of this, in turn, involves showing that a pair satisfies a product specification, and an abstraction satisfies a functional specification (as above). We also have to use induction to show that an iteration satisfies some specification parameterised on the naturals. We consider this example more fully in Section 4.4.

We do not throw away the original rule that t satisfies (x : τ)P when t : τ and P [t/x], however.

A significant benefit in writing specifications in this more structured form is conceptual — it is preferable to structure specifications for then the task of comprehension need not be duplicated unnecessarily for specification and program. Also, separate checks of well-formedness (i.e. type-checking here) and correctness, will involve some duplication of effort, so it is better to combine types and correctness properties. In order to be the basis of a useful program development methodology, it helps for our specifications and proofs to reflect the structure of the programs.

In this small example, the disadvantage of using propositions as specification is not so obvious. However, structure is essential for large specifications so we build it into the theory. Moreover, it is natural to incorporate equality in the definition of specification, rather than express it as a separate proposition. McKinna was led to the same conclusion in [McK92].

There is one final aspect of specifications which we must consider — equality. The kind of specifications with which we are concerned here are those which specify the input-output characteristics of programs. We are only interested in programs up to extensional equality. The alternative, in a type-theoretic setting, is to use an intensional equality and distinguish programs on the basis of syntactic form.

This would be unnatural here however, so we view specifications as inducing an equality on terms. This is a partial equivalence relation (per) on terms at the underlying type. A per is a symmetric and transitive relation on some set, or equivalently, an equivalence relation on a particular subset. The partiality is because not all terms (of the corresponding type) need satisfy a specification.

For example, the specification Π_{l:nonemptylist}(n : nat)Min(n, l) where the proposition Min(n, l) says that n is the minimum element in list l, is a refinement type over type list → nat. We want to regard functions f, f′ : list → nat as equal solutions of this specification if they give the same results for nonempty lists. Any program satisfying this specification must be defined on the empty list, but we are not interested in the value it takes there.

Moreover, it is a useful abbreviation in specifications themselves to write equality at a specification, =_{Π_{x:σ|P} (y:τ)Q}, where t =_{Π_{x:σ|P} (y:τ)Q} t′ means

∀x, x′ : σ.(x =_{σ} x′) ∧ P [x] ∧ P [x′] ⊃ (tx =_{τ} t′x′) ∧ Q[tx] ∧ Q[t′x′]

Now, we would attain some conceptual simplicity if specifications were to subsume types, satisfaction to subsume typing, and equality at a specification to subsume the usual equality at a type (which is often left implicit). For example, we use (n : nat)⊤ in place of nat, and Σ_{n:(n:nat)⊤}(b : bool)⊤ for nat×bool, where Σ_{x:ϕ|P} ψ abbreviates Σ_{x:(x:ϕ)P} ψ, and similarly for Π. At this point, we must cease to regard Π_{x:σ|P} (y : τ)Q as we did before, since the above convention means that the equality is only with respect to arguments in (x : σ)P, rather than σ as for (f : σ → τ) ∀x : σ . P ⊃ Q[fx/y].

We believe it is misleading to regard specifications a rich form of types, though, and refer to the specifications of this idealised specification language as refinement types. We regard types, rather, as being part of the programming language, and specifications as being constructed at a level above this.

In fact, we will take the denotation of refinement types to be a per. A specification therefore, is a refinement type, and denotes

• a set, together with

• a per over the set

We take a program in this calculus to denote

• an equivalence class of a per ϕ

The alternative would be to take a program as denoting an element of the domain of a per, but this would be unnatural because we would then be distinguishing programs beyond extensional equality.

So refinement types induce a per on the set of terms of the underlying type. The converse is not true, that is, not all pers of terms correspond to refinement types. For example, the per R on naturals, n R n′ ⟺ “both n and n′ are even, or both are odd”.

We use a notation for the equivalence classes of pers, by allowing refinement types on the variables in abstractions. For example, λn : even.n denotes a class in the per denoted by even → nat (functions in the corresponding set are equal if they give the same results for even arguments) but not nat → nat, and λn : nat.n denotes a class in both even → nat and nat → nat. The meaning of the equality t =_{ϕ} t′ is that t and t′ denote sets in the same equivalence class of per ϕ.

For refinement types ϕ and ϕ′ over the same underlying type, we want to consider refinements ϕ ⊑_{τ} ϕ′, to be thought of semantically as per inclusion (i.e. equality at ϕ′ implies equality at ϕ). We use the square ⊑_{τ} symbol to indicate an information ordering — the refinement of specifications over type τ. Note that this convention for refinement is the opposite direction to the usual subtyping relation.

4.3 The Calculus

We now give the syntax of the system and describe its judgements. Some syntactic results are then given and we give an operational intuition for the language.

4.3.1 Syntax

The idea is that we construct a theory of refinement types on top of an underlying λ^{×→} theory and a first-order logic theory. This is generated from a signature of types, constants and predicate symbols (in the underlying theory) and axioms (in the full theory). The well-formedness conditions on axioms will be explained in Section 4.3.4 below.

We construct the theory of refinement types from the same basic data as the first-order theories of lambda calculus. A λ_{(:)}-signature is the same as a first-order λ^{×→}-signature. We repeat the definition.

Definition 4.3.1 A λ_{(:)}-signature, Sg = ⟨G, K, F⟩, consists of:

• a collection, G, of ground types (ranged over by γ)

• a collection, K, of constant symbols (ranged over by k), each of which has an arity n and sort τ_{1}, …, τ_{n} → τ, which we write as k : τ_{1}, …, τ_{n} → τ.

• a collection, F, of predicate symbols (ranged over by F) each of which has an arity n and sort τ_{1}, …, τ_{n}, which we write as F : Pred (τ_{1}, …, τ_{n}).

Although we do not have arbitrary refinement types as primitive in a signature, we get much the same expressiveness using predicate symbols. For example, with the primitive type nat, we could have predicate Even : Pred (nat), and write even for the refinement type (n : nat) Even(n).

Definition 4.3.2 Let Sg = ⟨G, K, F⟩ be a λ_{(:)}-signature. The pre-expressions over Sg are given by a mutual recursion over (pre-) refinement types, terms and propositions:

\begin{aligned}ϕ & ::= & 1 | γ | Σ_{x:ϕ}ψ | Π_{x:ϕ}ψ | (x : ϕ)P \\ t & ::= & x | k(t_{1}, …, t_{n}) | ∗ | ⟨t, t′⟩ | λx : ϕ.t | π_{1}(t) | π_{2}(t) | tt′ \\ P & ::= & ⊥ | P ⊃ P′ | ∀x : ϕ.P | F (t_{1}, …, t_{n}) | t =_{ϕ} t′ | ϕ ⊑_{τ} ϕ′ \end{aligned}

The pre-contexts are:

Γ ::= ⟨⟩ | Γ, x : ϕ | Γ, P

We say that Γ ⊢ U is a pre-expression in context when Γ is a pre-context, U is a pre-expression, and F V (U) ⊆ Γ.

As for the previous calculi, we assume a countably infinite set of variables. We adopt the usual abbreviations of ϕ × ψ for Σ_{x:ϕ}ψ and ϕ → ψ for Π_{x:ϕ}ψ, when x ∉ F V (ψ), and use ϕ, ψ and χ as metavariables for refinement types. We sometimes abbreviate the assumption x : (x : ϕ)P, on abstractions or in contexts, as x : ϕ|P.

Conceptually, it is simpler not to distinguish types and refinement types as syntactic categories. In an informal sense which we will later make formal, refinement types should be viewed as being refinements of underlying types, so for example, if ϕ is a refinement of (we will just say ‘over’) σ, and ψ is over τ, then Σ_{x:ϕ}ψ is over σ × τ. Formally however, types are just refinement types with no logical information, that is, not containing any propositions. We use σ and τ as metavariables for types, and refer to the type underlying a refinement type.

Here we extend the variable convention of p. 33 so that by writing x : ϕ, where ϕ is over τ, we assume that x is drawn from the set of variables for type τ.

There is a term ∗ of unit type 1. After we introduce the equality judgement we will see that ∗ is the unique term at 1 up to equality. The meaning of the other refinement types in terms of satisfaction is that ⟨t, t′⟩ satisfies Σ_{x:ϕ}ψ when t satisfies ϕ and t′ satisfies ψ[t/x]; term t satisfies Π_{x:ϕ}ψ when for every t′ satisfying ϕ, tt′ is well-formed and satisfies ψ[t′/x]; and t satisfies (x : ϕ)P when it satisfies ϕ and the proposition P [t/x] holds.

We think of terms of the calculus as being simple specifications of terms in the underlying λ^{×→}. We will refer to terms of λ^{×→} as total terms. Terms have their usual meaning in the lambda calculus, except that an abstraction λx : ϕ.t should be thought of as a simple specification of terms λx : σ.t′ such that for all t′′ which satisfy ϕ, t′[t′′/x] satisfies t[t′′/x]. For example, λn : even.n specifies total terms of type nat → nat which are the identity on even arguments. The application (λx : ϕ.t)t′′ is only well-formed for arguments t′′ which satisfy ϕ so behaviour outwith ϕ is not constrained. Note that this means that although even → even is a refinement of the type nat → nat, the term λn : even.n does not itself have type nat → nat. Intuitively, we can say that a term t has refinement type ϕ if its behaviour ‘at ϕ’ is uniquely determined, that is, any two total terms which satisfy t are themselves equal at ϕ.

The propositions are a typed first-order predicate logic of equalities and typed refinements. In practice, we will almost always omit the subscript from ϕ ⊑_{τ} ϕ′. We use classical typed first-order logic as an example of a simple expressive logic.

Remark 4.3.3 Our choice of first-order classical logic is only significant insofar as it is an example of what we might call an extensional logic. In type theory, a distinction is often made between extensional and intensional equality [NPS90]. Terms are extensionally equal if they have the same input-output behaviour, as given by some proposition (hence, also called ‘propositional equality’), whereas intensional equality is a definitional equality which is generally decidable.

In this sense, intensional means ‘relating to syntactic form’, but there is a more general sense in which it is with respect to richer properties than input-output behaviour, such as time-complexity, and it is this kind of predicate we want to contrast with those used here.

Since we allow the propositional equality in properties, the system only makes sense for extensional predicates. We make essential use of the fact that for all terms t, t′ and propositions P, if t is extensionally equal to t′, then P [t/x] holds if and only if P [t′/x] does.

Since the converse clearly holds, we can express this as saying that we require Leibniz (satisfaction of the same predicates) and observational (same inputoutput behaviour) equality to coincide. It does not matter whether the logic is classical or intuitionistic.

This can be contrasted with, say, the use of an intensional logic such as the modal μ-calculus, where terms are viewed as transition systems through their reduction sequences.

4.3.2 Judgements

The main judgements of the calculus have the forms

Γ ⊢ t : ϕ
Γ ⊢ P

where the atomic propositions include Γ ⊢ t =_{ϕ} t′ and Γ ⊢ ϕ ⊑_{τ} ϕ′. Equality and refinement are not separate judgement classes from the other propositions. We will write Γ ⊢ ϕ = ϕ′ for the mutual refinement Γ ⊢ ϕ ⊑ ϕ′ and Γ ⊢ ϕ′ ⊑ ϕ.

All judgements are made in a context Γ of variable assumptions x : ϕ and propositions P. There are also mutually dependent well-formedness judgements

                              ⊢ Γ wf

Γ ⊢ ϕ : Ref (τ)
Γ ⊢ P wf

We say that a term t is well-formed in context Γ when there exists a refinement type ϕ such that Γ ⊢ t : ϕ. Note that ϕ need not be unique, though the underlying type is unique. We understand Γ ⊢ t : ϕ to mean that for all the variables in the context Γ, if they satisfy the relevant refinement types, then the term t has refinement type ϕ. Sometimes we write Γ ⊢ t, t′ : ϕ for Γ ⊢ t : ϕ and Γ ⊢ t′ : ϕ.

The well-formedness judgement for refinement types, Γ ⊢ ϕ : Ref (τ), says that refinement type ϕ in context Γ is over the type τ. We abbreviate ϕ : Ref (τ) as ϕ wf when the type τ is not significant. Although the extra information that ϕ is over τ is not required for the well-formedness of ϕ itself, it is used to check the well-formedness of refinements.

We use g as a metavariable for ‘syntactic environments’, that is, tuples of terms which satisfy the refinement types and propositions in the context. We will use the abbreviations g : Γ, g =_{Γ} g′ and t[g/Γ] to indicate simultaneous satisfaction, equality and substitution respectively.

Remark 4.3.4 We should not think of Ref (τ) as a power type of τ, since the refinement τ ⊑ ϕ is not the same as ϕ : Ref (τ). This is because the attribution of refinement types is not contravariant at function types. For example, since nat → nat ⋢ even → nat we cannot regard even → nat as a subtype of nat → nat even though it is a refinement type. The difference between power types and refinement types can be seen by considering encodings in higher-order logic. The power types of τ may be encoded as τ → Prop, whereas refinement types of τ would correspond to τ → τ → Prop, since specifications comprise a (partial) equality relation. The λ_{(:)}-calculus can be seen as a convenient formalism for such relations.

4.3.3 λ_{(:)}-Axiom Systems

A λ_{(:)}-axiom system consists of a collection of first-order axioms over some signature. There is a crucial difference from Definition 2.3.3, however. Since refinement types can appear in terms, well-formedness involves logical reasoning and, in particular, can depend on the axioms. If axioms themselves are to be well-formed, therefore, we must have some dependencies among the axioms. The problem is that with axiom schemas it is possible for one instance to be needed to prove the well-formedness of another. For example, an induction principle over the naturals is schematic in some proposition P [n] for n : nat, but the well-formedness of P might itself require induction.

This problem is common to all logics in which well-formedness depends on provability. Rather than introduce a hierarchy of axioms, the solution we adopt here is to drop the requirement that axioms are well-formed, and instead only check for this when they are used in a proof.

It is natural to give axioms in a deliverables style [McK92]. If k is a unary constant with sort σ → τ, then we give axioms of the form “if the argument satisfies some specification then the result satisfies some specification”. We write k : ϕ → ψ to mean that if t has refinement type ϕ then k(t) has refinement type ψ. In general, we allow the refinement types to be open in some context. (Note that this is not a judgement form (see below) but an axiom which we will use in side-conditions.) Thus we have the following definition. Definition 4.3.5 A λ_{(:)}-axiom system, ⟨Sg, Ax⟩, consists of a λ_{(:)}-signature, Sg, and a collection of axioms Ax formed from pre-contexts and pre-expressions over Sg. Axioms are of two forms:

• propositions in context, Γ ⊢ P

• axioms for constants, Γ ⊢ k : ϕ_{1}, …, ϕ_{n} → ψ.

We do not put any well-formedness requirements on axioms, but check for well-formedness at the point of using the axiom in a proof. A similar convention is adopted by Pitts for dependently-typed algebraic theories [Pit95].

Hence, although we intend that when k has sort τ_{1}, …, τ_{n} → τ, that we have Γ ⊢ ϕ_{i} : Ref (τ_{i}) (i = 1, …, n) and Γ ⊢ ψ : Ref (τ) we do not enforce it in the axiom system. We could, for example, have required for axiom Γ ⊢ P that F V (P) ⊆ Γ but this will follow automatically from the well-formedness check when the axiom is used, and similarly for constant axioms.

Note that the sorting k : τ → τ′ and axiom k : ϕ → ψ do not say that the unary constant k is a well-formed term without the necessary number of arguments. For t : τ, we have k(t) : τ′, and if t : ϕ then k(t) : ψ. We do, however, consider sortings as axioms.

Allowing arbitrary propositions as axioms subsumes the definition of λ^{×→}- axiom systems, since we can include equations in context between determined terms, Γ ⊢ t =_{ϕ} t′.

Remark 4.3.6 It is not clear that it is necessary to allow arbitrary propositions as axioms in Definition 4.3.5. We will show later (in Section 4.3.5) that induction schemas follow from the axiomatisation of the corresponding constant for recursion. This suggests that we may only need propositional axioms in order to axiomatise the predicate symbols. It seems that these axioms can always be given in the form Γ ⊢ F (t) or Γ ⊢ ¬F (t).

The axioms for constants could be given in the form Γ, x_{1} : ϕ_{1}, …, x_{n} : ϕ_{n} ⊢ k(x_{1}, …, x_{n}) : ψ, which is actually equivalent to the, perhaps more natural, general form Γ ⊢ k(t_{1}, …, t_{n}) : ψ.

4.3.4 Rules of the Calculus

Definition 4.3.7 Let ⟨Sg, Ax⟩ be a λ_{(:)}-axiom system. We define the theorems of ⟨Sg, Ax⟩ to be the judgements which can be inferred using the rules of Figures 4.1 to 4.10. We write ⟨Sg, Ax⟩ ▷ J to indicate that judgement J is a theorem of axiom system ⟨Sg, Ax⟩.

Note that we consider judgements of well-formedness to also be theorems, since they involve logical reasoning.

The rules of the calculus are listed in Figures 4.1-4.10. One distinctive feature of the calculus is the mutual dependencies of the different syntactic categories, and hence of the different judgement classes. Refinement types can contain propositions, which can contain terms, and these in turn can contain refinement types in the abstractions.

In Figure 4.1 we give the rules for generating theorems from a λ_{(:)}-axiom system. It is here that we check that an axiom is well-formed before it can be used. The substitution rule is for an arbitrary basic judgement, B. The rule is quite simple because we have explicit congruence rules for the equalities. By encoding B as a proposition, and using the rules for implication and universal quantification we can actually derive a more general rule:

\begin{matrix}Γ, x : ϕ, Γ′ ⊢ B & Γ ⊢ t : ϕ \\ Γ, Γ′[t/x] ⊢ B[t/x] \end{matrix}

Next we describe the well-formedness rules, starting with contexts. The empty context is well-formed, and there are two rules for extending an existing context. Figures 4.2 and 4.3 give the well-formedness rules for contexts and refinement types respectively.

The well-formedness rules for refinement types essentially involve stripping off the logic while checking that everything fits together correctly. There are checks on the well-formedness of the context for the base cases so as to ensure that all provable judgements are well-formed. Similar conditions are made for the base cases of the other judgement classes.

It is straightforward to formulate well-formedness rules for propositions. These are given in Figure 4.4. In proving the well-formedness of the implication P ⊃ P′, we can assume the truth of P for proving the well-formedness of P′. For the equality t =_{ϕ} t′ to be well-formed we require that t and t′ are both well-formed and have refinement types over the same type as ϕ. We do not require that t and t′ have refinement type ϕ. This allows us to express the refinement typing t : ϕ as the proposition t =_{ϕ} t′. The appeal to refinement typing is why well-formedness

Axioms  {Γ ⊢ P wf \atop Γ ⊢ P} (Γ ⊢ P ∈ Ax)
Weakening  \begin{matrix}Γ_{1}, Γ_{2} ⊢ B & Γ_{1} ⊢ ϕ wf \\ Γ_{1}, x : ϕ, Γ_{2} ⊢ B \end{matrix}
Γ_{1}, x_{1} : ϕ_{1}, Γ_{2}, x_{2} : ϕ_{2}, Γ_{3} ⊢ B Γ_{1} ⊢ ϕ_{2} wf Γ_{1}, x_{2} : ϕ_{2}, Γ_{2}, x_{1} : ϕ_{1}, Γ_{3} ⊢ B (x^{1} ∉ Γ^{2}, x^{2} : ϕ^{2}) Permutation
Substitution  \begin{matrix}Γ, x : ϕ ⊢ B & Γ ⊢ t : ϕ \\ Γ ⊢ B[t/x] \end{matrix}

Figure 4.1: Theorems Generated from a λ_{(:)}-Axiom System ⟨Sg, Ax⟩

Empty Context  ⊢ ⟨⟩ wf
Variable Assumption {Γ ⊢ ϕ wf \atop ⊢ Γ, x : ϕ wf} (x ∉ Γ)
{Γ ⊢ P wf \atop ⊢ Γ, P wf} Propositional Assumption

Figure 4.2: Well-formedness of Contexts

Unit  {⊢ Γ wf \atop Γ ⊢ 1 : Ref (1)}
Γ ⊢ ϕ : Ref (σ) Γ, x : ϕ ⊢ ψ : Ref (τ)
Γ ⊢ Σ_{x:ϕ}ψ : Ref (σ × τ)

Product

Γ ⊢ ϕ : Ref (σ) Γ, x : ϕ ⊢ ψ : Ref (τ)
Γ ⊢ Π_{x:ϕ}ψ : Ref (σ → τ)

Function

Γ ⊢ ϕ : Ref (τ) Γ, x : ϕ ⊢ P wf
Γ ⊢ (x : ϕ)P : Ref (τ)

Refinement Types

Ground Types  {⊢ Γ wf \atop Γ ⊢ γ : Ref (γ)} (γ ∈ G)

Figure 4.3: Well-formedness of Refinement Types involves logical reasoning, and this propagates through the well-formedness rules for the other syntactic categories. Similarly, the refinement ϕ ⊑ ϕ′ is only wellformed when ϕ and ϕ′ are over the same type.

In Figures 4.5 and 4.6 we give the refinement typing rules, which also serve as well-formedness rules for the terms. Where this differs from the simply-typed lambda calculus is in the logical reasoning which pervades the rules. This is evident in the Constants rule, where well-formedness uses a logical axiom. If constant symbol k has axiom Γ′ ⊢ k : ϕ_{1}, …, ϕ_{n} → ψ ∈ Ax, then we infer a refinement typing in the more general context Γ, where Γ′ ⊆ Γ. If Γ ⊢ t_{i} : ϕ_{i} for i = 1, n, then we infer Γ ⊢ k(t_{1}, …, t_{n}) : ψ. The reason for the more general context is so that from an axiom like n : nat ⊢ k : ϕ[n] → ψ[n], we can infer that x : ϕ[2] ⊢ k(x) : ψ[2], by using the context n : nat, n = 2. (In fact, in the case of refinement typing, we can derive the more general rule for constants from a simpler one, but this is not the case for the rule of Constant Equations below so we keep both rules in the same form.)

The refinement typing rules are the natural generalisations of the usual typing rules for the simply-typed lambda calculus with products. For example, the introduction rule for abstractions is

{Γ, x : ϕ ⊢ t : ψ \atop Γ ⊢ λx : ϕ.t : Π_{x:ϕ}ψ}

For λx : ϕ.t to be well-formed in context Γ, it is sufficient, but not necessary that t is well-formed in context Γ, x : ϕ.

This is because our notion of well-formedness is ‘having some refinement type’. For example, we have λn : nat.(λm : even.m)n : even → nat, although the body is not well-formed in the context n : nat.

We have the obvious introduction rule for proving that a term inhabits a refinement type, and a weakening rule:

\begin{matrix}Γ ⊢ t : ϕ & Γ ⊢ P [t/x] \\ Γ ⊢ t : (x : ϕ)P \end{matrix}  \begin{matrix}Γ ⊢ t : ϕ′ & Γ ⊢ ϕ ⊑ ϕ′ \\ Γ ⊢ t : ϕ \end{matrix}

The corresponding elimination rule — concluding that t : ϕ from t : (x : ϕ)P — follows from the weakening rule and the refinement rules which we give below.

One further rule is

{Γ ⊢ t =_{ϕ} t′ \atop Γ ⊢ t : ϕ}

Inferring a refinement typing from an equality may seem strange, but it saves us a few rules. The reason for this is that in proving refinement typings and equalities

Falsehood  {⊢ Γ wf \atop Γ ⊢ ⊥ wf}
Γ ⊢ P wf Γ, P ⊢ P′ wf
Γ ⊢ P ⊃ P′ wf

Implication

{Γ, x : ϕ ⊢ P wf \atop Γ ⊢ ∀x : ϕ.P wf} Universal Quantification
Γ ⊢ t_{1} : ϕ_{1} ⋯ Γ ⊢ t_{n} : ϕ_{n} Γ ⊢ ϕ_{1} : Ref (τ_{1}) ⋯ Γ ⊢ ϕ_{n} : Ref (τ_{n}) Γ ⊢ F (t_{1}, …, t_{n}) wf  (F : Pred (τ_{1}, …, τ_{n}) ∈ F) Predication
Γ ⊢ t : ψ Γ ⊢ t′ : ψ′ Γ ⊢ ψ, ψ′, ϕ : Ref (τ)
Γ ⊢ t =_{ϕ} t′ wf

Equality

Γ ⊢ ϕ : Ref (τ) Γ ⊢ ϕ′ : Ref (τ)
Γ ⊢ ϕ ⊑_{τ} ϕ′ wf

Refinement Figure 4.4: Well-formedness of Propositions

Variables  {⊢ Γ, x : ϕ, Γ′ wf \atop Γ, x : ϕ, Γ′ ⊢ x : ϕ}
Γ ⊢ ϕ_{1} : Ref (τ_{1}) ⋯ Γ ⊢ ϕ_{n} : Ref (τ_{n}) Γ ⊢ ψ : Ref (τ) Γ ⊢ t_{1} : ϕ_{1} ⋯ Γ ⊢ t_{n} : ϕ_{n} Γ ⊢ k(t_{1}, …, t_{n}) : ψ  {Γ′ ⊢ k : ϕ_{1}, …, ϕ_{n} → ψ ∈ Ax ; ; k : τ_{1}, …, τ_{n} → τ ∈ K; ; Γ′ ⊆ Γ Constants
Unit  {⊢ Γ wf \atop Γ ⊢ ∗ : 1}

\begin{aligned}Γ ⊢ t : ϕ & Γ ⊢ t′ : ψ[t/x] & Γ, x : ϕ ⊢ ψ wf \\ & Γ ⊢ ⟨t, t′⟩ : Σ_{x:ϕ}ψ \end{aligned}

{Γ ⊢ t : Σ_{x:ϕ}ψ \atop Γ ⊢ π_{1}(t) : ϕ}  {Γ ⊢ t : Σ_{x:ϕ}ψ \atop Γ ⊢ π_{2}(t) : ψ[π_{1}(t)/x]}

Product Terms

Function Terms  {Γ, x : ϕ ⊢ t : ψ \atop Γ ⊢ λx : ϕ.t : Π_{x:ϕ}ψ} ; \begin{matrix}Γ ⊢ t : Π_{x:ϕ}ψ & Γ ⊢ t′ : ϕ \\ Γ ⊢ tt′ : ψ[t′/x] \end{matrix}

Figure 4.5: Refinement Typings

\begin{matrix}Γ ⊢ t : ϕ & Γ ⊢ P [t/x] \\ Γ ⊢ t : (x : ϕ)P \end{matrix} Refinement Type Introduction

Equality  {Γ ⊢ t =_{ϕ} t′ \atop Γ ⊢ t : ϕ}

Weakening  \begin{matrix}Γ ⊢ t : ϕ′ & Γ ⊢ ϕ ⊑ ϕ′ \\ Γ ⊢ t : ϕ \end{matrix}

Figure 4.6: Refinement Typings cont.

we need to be able to combine assumptions on subterms. Since equalities are subscripted with a refinement type, the rule lets us use equality rules to prove a refinement typing. For example, the congruence equation for abstractions is

\begin{matrix}Γ, x : ϕ ⊢ P wf & Γ, x : ϕ, P ⊢ t =_{ψ} t′ \\ Γ ⊢ λx : (x : ϕ)P.t =_{Π_{x:(x:ϕ)P} ψ} λx : ϕ.t′ \end{matrix}

which lets us prove that λn : even.n =_{even→even} λn : nat.n, and so we can infer that λn : nat.n : even → even. The more general inference for ϕ′ ⊑ ϕ, that λx : ϕ.t =_{Π_{x:ϕ}ψ} λx : ϕ′.t′, is not actually sound. For example, the term λf : even → nat.f does not have the refinement type (nat → nat) → (nat →

nat).

Figures 4.7 and 4.8 give equality rules for terms. In combination with the rule of Equality, the Congruence Equations for abstractions lets us prove (as in [Asp95]), for example, that λn : nat.n : even → even even though nat → nat and even → even are incomparable (which is an obstacle for some subtyping systems).

The η-equalities for abstractions and pairs have unconventional hypotheses, enabling us to combine logical and typing assumptions.

{Γ, x : ϕ ⊢ tx : ψ \atop Γ ⊢ λx : ϕ.tx =_{Π_{x:ϕ}ψ} t} (x ∉ F V (t)) \begin{matrix}Γ ⊢ π_{1}(t) : ϕ & Γ ⊢ π_{2}(t) : ψ[π_{1}(t)/x] \\ Γ ⊢ ⟨π_{1}(t), π_{2}(t)⟩ =_{Σ_{x:ϕ}ψ} t \end{matrix}

The usual hypothesis for the abstraction rule would be Γ ⊢ t : Π_{x:ϕ}ψ. The following example makes essential use of this rule.

Example 4.3.8 We use the η-equality for abstractions to infer a refinement typing.

 f : nat → nat, ∀x : nat.Even(fx), n : nat ⊢ fn : nat
      f : nat → nat, ∀x : nat.Even(fx), n : nat ⊢ Even(fn)
    f : nat → nat, ∀x : nat.Even(fx), n : nat ⊢ fn : even
  f : nat → nat, ∀x : nat.Even(fx) ⊢ f =_{nat→even} λn : nat.fn

Function Eqs. (η)

     f : nat → nat, ∀x : nat.Even(fx) ⊢ f : nat → even

Similarly, we use the η rule for pairs in order to prove, for example, that

z : σ × τ, P [π_{1}z], Q[π_{2}z] ⊢ z : (x : σ)P × (y : τ)Q

The well-formedness hypothesis in the rule of Logical Congruence is important since, with stratified equalities, we require equality at the appropriate refinement type.

Remark 4.3.9 We can define singleton types. For t : ϕ, write {t}_{ϕ} for the refinement type (x : ϕ) x =_{ϕ} t. Then we have Γ ⊢ P [t/x] iff Γ, x : {t}_{ϕ} ⊢ P. We conjecture that the λ_{(:)}-calculus is a conservative extension of Aspinall’s singleton types calculus [Asp95].

Example 4.3.10

n : nat, Even(n) ⊢ n : nat nat ⊑ (n : nat)Even(n) ^{Ref.Types (R)} n : even ⊢ n : even n : even ⊢ n =_{even} n ^{Reflexivity}

               λn : even.n =_{even→even} λn : nat.n

Cong.Eqs.

                  λn : nat.n : even → even

Equality

Using the symmetry rule we can deduce the symmetric forms for the congruences. e.g. if t =_{Σ_{x:ϕ}ψ} t′ then π_{2}(t) =_{ψ[π_{1}(t′)]} π_{2}(t′).

Figure 4.9 lists the refinement rules. These are of two kinds — ‘structural’ and logical. The obvious structural rules are

{⊢ Γ wf \atop Γ ⊢ 1 ⊑ 1} \begin{matrix}Γ ⊢ ϕ ⊑ ϕ′ & Γ, x : ϕ ⊢ ψ ⊑ ψ′ \\ Γ ⊢ Σ_{x:ϕ}ψ ⊑ Σ_{x:ϕ′}ψ′ \end{matrix}  \begin{matrix}Γ ⊢ ϕ′ ⊑ ϕ & Γ, x : ϕ ⊢ ψ ⊑ ψ′ \\ Γ ⊢ Π_{x:ϕ}ψ ⊑ Π_{x:ϕ′}ψ′ \end{matrix}

The interesting rules, however, are for refinement involving propositions. We must say when an arbitrary refinement type is a refinement of a type with a proposition, and when it refines to one.

\begin{matrix}Γ ⊢ ϕ ⊑ ψ & Γ, x : ψ ⊢ P \\ Γ ⊢ (x : ϕ)P ⊑ ψ \end{matrix}  {Γ, x : ψ, Q ⊢ x : ϕ \atop Γ ⊢ ϕ ⊑ (x : ψ)Q}

The only other refinement rule is transitivity of refinement.

{Γ ⊢ t : ϕ \atop Γ ⊢ t =_{ϕ} t} ; {Γ ⊢ t =_{ϕ} t′ \atop Γ ⊢ t′ =_{ϕ} t} ; \begin{matrix}Γ ⊢ t =_{ϕ} t′ & Γ ⊢ t′ =_{ϕ} t′′ \\ Γ ⊢ t =_{ϕ} t′′ \end{matrix} Equational Reasoning
\begin{matrix}\begin{matrix}Γ, x : ϕ ⊢ t : ψ & Γ ⊢ t′ : ϕ \\ Γ ⊢ (λx : ϕ.t)t′ =_{ψ[t′/x]} t[t′/x] \end{matrix} & (β) \\ {Γ, x : ϕ ⊢ tx : ψ \atop Γ ⊢ λx : ϕ.tx =_{Π_{x:ϕ}ψ} t} (x ∉ F V (t)) & (η) \end{matrix} Function Equations
\begin{matrix}\begin{matrix}Γ ⊢ t_{1} : ϕ_{1} & Γ ⊢ t_{2} : ϕ_{2} \\ Γ ⊢ π_{i}⟨t_{1}, t_{2}⟩ =_{ϕ_{i}} t_{i} \end{matrix} & (β) \\ \begin{matrix}Γ ⊢ π_{1}(t) : ϕ & Γ ⊢ π_{2}(t) : ψ[π_{1}(t)/x] \\ Γ ⊢ ⟨π_{1}(t), π_{2}(t)⟩ =_{Σ_{x:ϕ}ψ} t \end{matrix} & (η) \end{matrix} Product Equations
Unit Equation  {Γ ⊢ t : 1 \atop Γ ⊢ t =_{1} ∗}

Figure 4.7: Equality Rules

Γ ⊢ ϕ_{1} : Ref (τ_{1}) ⋯ Γ ⊢ ϕ_{n} : Ref (τ_{n}) Γ ⊢ ψ : Ref (τ) Γ ⊢ t_{1} =_{ϕ_{1}} t′_{1} ⋯ Γ ⊢ t_{n} =_{ϕ_{n}} t′_{n} Γ ⊢ k(t_{1}, …, t_{n}) =_{ψ} k(t′_{1}, …, t′_{n})  {Γ′ ⊢ k : ϕ_{1}, …, ϕ_{n} → ψ ∈ Ax ; ; k : τ_{1}, …, τ_{n} → τ ∈ K; ; Γ′ ⊆ Γ Constant Equations

\begin{matrix}Γ, x : ϕ ⊢ P wf & Γ, x : ϕ, P ⊢ t =_{ψ} t′ \\ Γ ⊢ λx : (x : ϕ)P.t =_{Π_{x:(x:ϕ)P} ψ} λx : ϕ.t′ \end{matrix}

\begin{matrix}Γ ⊢ t_{1} =_{Π_{x:ϕ}ψ} t′_{1} & Γ ⊢ t_{2} =_{ϕ} t′_{2} \\ Γ ⊢ t_{1}t_{2} =_{ψ[t_{2}/x]} t′_{1}t′_{2} \end{matrix}

\begin{matrix}Γ ⊢ t_{1} =_{ϕ} t′_{1} & Γ ⊢ t_{2} =_{ψ[t_{1}/x]} t′_{2} \\ Γ ⊢ ⟨t_{1}, t_{2}⟩ =_{Σ_{x:ϕ}ψ} ⟨t′_{1}, t′_{2}⟩ \end{matrix}

{Γ ⊢ t =_{Σ_{x:ϕ}ψ} t′ \atop Γ ⊢ π_{1}(t) =_{ϕ} π_{1}(t′)}  {Γ ⊢ t =_{Σ_{x:ϕ}ψ} t′ \atop Γ ⊢ π_{2}(t) =_{ψ[π_{1}(t)/x]} π_{2}(t′)}

Congruence Equations

\begin{aligned}Γ ⊢ t =_{ϕ} t′ & Γ ⊢ P [t/x] & Γ, x : ϕ ⊢ P wf \\ & Γ ⊢ t =_{(x:ϕ)P} t′ \end{aligned} Logical Congruence
Weakening  \begin{matrix}Γ ⊢ t =_{ϕ′} t & Γ ⊢ ϕ ⊑ ϕ′ \\ Γ ⊢ t =_{ϕ} t′ \end{matrix}

Figure 4.8: Equality Rules cont.

Unit  {⊢ Γ wf \atop Γ ⊢ 1 ⊑ 1}
Product  \begin{matrix}Γ ⊢ ϕ ⊑ ϕ′ & Γ, x : ϕ ⊢ ψ ⊑ ψ′ \\ Γ ⊢ Σ_{x:ϕ}ψ ⊑ Σ_{x:ϕ′}ψ′ \end{matrix}
Function  \begin{matrix}Γ ⊢ ϕ′ ⊑ ϕ & Γ, x : ϕ ⊢ ψ ⊑ ψ′ \\ Γ ⊢ Π_{x:ϕ}ψ ⊑ Π_{x:ϕ′}ψ′ \end{matrix}
\begin{matrix}\begin{matrix}Γ ⊢ ϕ ⊑ ψ & Γ, x : ψ ⊢ P \\ Γ ⊢ (x : ϕ)P ⊑ ψ \end{matrix} & (L) \\ {Γ, x : ψ, Q ⊢ x : ϕ \atop Γ ⊢ ϕ ⊑ (x : ψ)Q} & (R) \end{matrix} Refinement Types
Transitivity  \begin{matrix}Γ ⊢ ϕ ⊑ ϕ′ & Γ ⊢ ϕ′ ⊑ ϕ′′ \\ Γ ⊢ ϕ ⊑ ϕ′′ \end{matrix}

Figure 4.9: Refinement Rules

Conjunction  \begin{matrix}Γ ⊢ P & Γ ⊢ Q \\ Γ ⊢ P ∧ Q \end{matrix} ; {Γ ⊢ P ∧ Q \atop Γ ⊢ P}  {Γ ⊢ P ∧ Q \atop Γ ⊢ Q}
\begin{matrix}Γ ⊢ P & Γ ⊢ Q wf \\ Γ ⊢ P ∨ Q \end{matrix}  \begin{matrix}Γ ⊢ P wf & Γ ⊢ Q \\ Γ ⊢ P ∨ Q \end{matrix}

\begin{aligned}Γ ⊢ P ∨ Q & Γ, P ⊢ R & Γ, Q ⊢ R \\ & Γ ⊢ R \end{aligned}

Disjunction

Implication {Γ, P ⊢ Q \atop Γ ⊢ P ⊃ Q}  \begin{aligned}Γ ⊢ P & Γ ⊢ P ⊃ Q \\ & Γ ⊢ Q \end{aligned}
\begin{matrix}Γ ⊢ ∀x : ϕ.P & Γ ⊢ t : ϕ \\ Γ ⊢ P [t/x] \end{matrix}  {Γ, x : ϕ ⊢ P \atop Γ ⊢ ∀x : ϕ.P} Universal Quantification
Falsehood {Γ ⊢ P wf \atop Γ, ⊥ ⊢ P} {Γ, ¬P ⊢ ⊥ \atop Γ ⊢ P}
Assumptions  {Γ ⊢ P wf \atop Γ, P ⊢ P}

\begin{aligned}Γ ⊢ t =_{ϕ} t′ & Γ, x : ϕ ⊢ P wf & Γ ⊢ P [t/x] \\ & Γ ⊢ P [t′/x] \end{aligned}

{Γ ⊢ t : (x : ϕ)P \atop Γ ⊢ P [t/x]}

Elimination Rules Figure 4.10: Natural Deduction Rules in a Theory of Refinement Types

Finally, the rules of the logic are given in Figure 4.10. This is a natural deduction presentation of a typed classical predicate logic of equalities and refinements. Assumptions in context can be used via Assumptions or Elimination Rules.

In order that when Γ ⊢ P is derivable, we have well-formedness of P in Γ, some rules (for false and assumptions) have explicit well-formedness hypotheses. This prevents us proving non well-formed equalities from ⊥.

Contexts differ from the usual formulations of typed lambda calculi since they contain propositions. The two forms of assumption are combined in the one context to make explicit the mutual dependencies. This is illustrated by the two introduction rules:

{Γ, P ⊢ Q \atop Γ ⊢ P ⊃ Q}  {Γ, x : ϕ ⊢ P \atop Γ ⊢ ∀x : ϕ.P}

We need a refinement typing for the ∀-elimination rule

\begin{matrix}Γ ⊢ ∀x : ϕ.P & Γ ⊢ t : ϕ \\ Γ ⊢ P [t/x] \end{matrix}

and refinement typings are also used to infer propositions with the rules

\begin{aligned}Γ ⊢ t =_{ϕ} t′ & Γ, x : ϕ ⊢ P wf & Γ ⊢ P [t/x] \\ & Γ ⊢ P [t′/x] \end{aligned} {Γ ⊢ t : (x : ϕ)P \atop Γ ⊢ P [t/x]}

4.3.5 Booleans and Naturals

We give axioms for booleans and naturals which combine the typing and logical rules of Chapter 2.

We give the λ_{(:)}-axiom system for booleans in Figure 4.11. There is one ground

type bool, and constants true : bool, false : bool and if   then   else :

bool, τ, τ → τ, for each type τ. The axiom schema for conditionals is given as

if then else : P + P′, (x : ϕ) P ⊃ Q[x], (y : ϕ) P′ ⊃ Q[y] → (z : ϕ) Q[z]

where P +P′ abbreviates (b : bool)b = true ⊃ P ∧ b = false ⊃ P′. The axiom says that if the truth of the boolean condition implies P, and its falsity implies P′, and under these assumptions we can infer that the respective branches have refinement type (z : ϕ)Q, then the conditional as a whole has this refinement type.

In fact, this axiom implies the others. A particular case of this axiom is

Γ, b = true ⊢ t : ψ Γ, b = false ⊢ t′ : ψ

                      Γ ⊢ if b then t else t′ : ψ

                            true : bool
                            false : bool
                  if  then  else : bool, τ, τ → τ

Sortings

                x : ϕ, y : ψ ⊢ iftruethen x else y =_{ϕ} x
                x : ϕ, y : ψ ⊢ iffalsethen x else y =_{ψ} y

      f : bool → ψ, b : bool ⊢ f(b) =_{ψ} if b then f(true) else f(false)

if then else : P +P′, (x : ϕ) P ⊃ Q[x], (y : ϕ) P′ ⊃ Q[y] → (z : ϕ) Q[z] Axioms

Figure 4.11: Axiom System for Booleans

This could be expressed more elegantly using sums, which we have not studied here.

In Figure 4.12 we give the λ_{(:)}-axiom system for naturals, making use of the singleton type notation from Remark 4.3.9. There are the zero and successor constants, and two constants for recursion. There are refinement typing and equational axioms for each form of recursion.

We give constants for both primitive and well-founded recursion. Primitive recursion enables us to write simple terminating programs which loop through a finite set of the values of some type, such that at each stage the program has access to the computation on the previous value. This is the functional equivalent of the for-loop. We use the constant natrec for primitive recursion over the naturals.

However, many programs are most naturally expressed by a form of recursion for which, at each stage of looping over the values of some type, the program has access to computations on all previous values. For example, the recursive call of the merge sort algorithm is not to the tail of a list, but to a sublist. What we require is the functional equivalent of the while-loop. The problem is that, in general, while loops do not terminate, and if we add a construct for full recursion to the simply-typed λ-calculus, then this results in inconsistency.^{2}

2If we have constants mu_{τ} : (τ → τ) → τ such that f : τ → τ ⊢ f(mu_{τ} (f)) =_{τ} mu_{τ} (f), then we can prove all well-typed equations. Let not : bool → bool and eq : bool → bool → bool be the negation and equality functions, respectively. We can use the η-equality for conditionals to prove that b : bool ⊢ eq b b = true and b : bool ⊢ eq b not(b) = false. By substituting mu_{bool}(not) for b, we can prove that true = false.

                              0 : nat
                          succ : nat → nat
                  natrec : τ, (nat → τ → τ), nat → τ
             natwfrec : (nat → (nat → τ) → τ) → nat → τ

Sortings

n : nat ⊢ natrec : ϕ[0], Π_{n:nat}ϕ[n] → ϕ[succ n], {n}_{nat} → ϕ[n]
Γ ⊢ z : ϕ[0] Γ ⊢ s : Π_{n:nat}ϕ[n] → ϕ[succ n]
Γ ⊢ natrec z s 0 =_{ϕ[0]} z
Γ ⊢ z : ϕ[0] Γ ⊢ s : Π_{n:nat}ϕ[n] → ϕ[succ n]

        Γ, n : nat ⊢ natrec z s (succ n) =_{ϕ[succ n]} s n (natrec z s n)

natwfrec : (Π_{x:nat}(Π_{z<x}ϕ[z]) → ϕ[x]) → Π_{x:nat}ϕ[x]

g : Π_{x:nat}(Π_{z<x}ϕ[z]) → ϕ[x], n : nat ⊢ natwfrec g n =_{ϕ[n]} g n (natwfrec g)

Axioms

Figure 4.12: Axiom System for Naturals

The solution is to restrict the recursion to loops which terminate. This can be done by defining a well-founded order on the data which the recursion is over, such that at each stage the computation on a value can only make use of computations on values lower in the order. This form of recursion is known as well-founded recursion [Nor88]. We will only use well-founded recursion over the naturals with the usual less-than ordering. We write Π_{z<x}ϕ[z] for Π_{z:nat|(z<x)}ϕ[z].

Rather than separate the proof of termination from well-formedness, however, we build it in by defining a constant, natwfrec, which can only construct terminating loops. The termination requirement can be expressed using refinement types.

The sorting is

             natwfrec : (nat → (nat → τ) → τ) → nat → τ

and the axiom is given as a refinement typing:

natwfrec : (Π_{x:nat}(Π_{z<x}ϕ[z]) → ϕ[x]) → Π_{x:nat}ϕ[x]

Recursion is formulated, without sacrificing termination, by

g : Π_{x:nat}(Π_{z<x}ϕ[z]) → ϕ[x], n : nat ⊢ natwfrec g n =_{ϕ[n]} g n (natwfrec g)

If f : nat → τ is given recursively as f(n) = t[n, f] where the body of the loop, t[n, f], is such that f is applied to values smaller than n, then we can define f as natwfrec (λn.λf.t). Then,

f n ≡ natwfrec (λn.λf.t) n
= t[n, natwfrec (λn.λf.t) ]
≡ t[n, f]

so that f n can be thought of as looping to t[n, f].

The induction rules of Chapter 2 can be derived. For n : nat ⊢ P [n] wf we derive the rule of mathematical induction:

{P [0] \atop ∗ : (z : 1)P [0]} ∀n : nat . P [n] ⊃ P [succ n]

λn : nat.λz : 1.∗ : Π_{n:nat}(z : 1)P [n] → (z : 1)P [succ n]
n : nat ⊢ natrec ∗ (λn : nat.λz : 1.∗) n : (z : 1)P [n]
∀n : nat.P [n]

Similarly, we can derive well-founded induction, and computational induction for natwfrec: for g : nat → (nat → τ) → τ and x : τ, n : nat ⊢ P [x, n] wf,

∀n : nat.(∀n′ < n.P [natwfrec g n′]) ⊃ P [g (natwfrec g n)]

                      ∀n : nat.P [natwfrec g n]

In fact, as noted on p. 47, well-founded induction follows from mathematical induction which, in turn, can be derived from the axiom for natrec.

Although constants are added in the simple type theory, axioms are given using refinement types. For example, although natwfrec g is always defined for g of appropriate type, the recursion equation for natwfrec only holds when g has the appropriate refinement type. Semantically, natwfrec is interpreted as a map from the set of the underlying type, but the interpretation is only constrained on the refinement type.

4.3.6 Metatheory

We prove a few syntactic results about the calculus. Some of the following results will be needed in Section 4.5 for proving completeness with respect to the semantics. Other standard metatheoretic results (not listed here) can be deduced from completeness.

Lemma 4.3.11 The following is derivable:

\begin{matrix}Γ ⊢ t : ϕ & Γ, x : ϕ ⊢ P \\ Γ ⊢ t : (x : ϕ)P \end{matrix}

Proof:

\begin{matrix}Γ ⊢ t : ϕ & \begin{matrix}Γ ⊢ ϕ ⊑ ϕ & Γ, x : ϕ ⊢ P \\ Γ ⊢ (x : ϕ)P ⊑ ϕ \end{matrix} \\ Γ ⊢ t : (x : ϕ)P \end{matrix}

We use this to show that the rule of Refinement Type Introduction is derivable.

Lemma 4.3.12 The rule

\begin{matrix}Γ ⊢ t : ϕ & Γ ⊢ P [t/x] \\ Γ ⊢ t : (x : ϕ)P \end{matrix}

is derivable.

Proof:

\begin{matrix}\begin{matrix}{Γ ⊢ t : ϕ \atop Γ ⊢ t : \{t\}_{ϕ}} & {Γ ⊢ P [t/x] \atop Γ, x : \{t\}_{ϕ} ⊢ P} \\ Γ ⊢ t : (x : \{t\}_{ϕ})P \end{matrix} & Γ ⊢ ϕ ⊑ \{t\}_{ϕ} \\ Γ ⊢ t : (x : ϕ)P \end{matrix}

It is an easy proof to show that for well-formed refinement types Γ ⊢ ϕ, we have reflexivity of refinement Γ ⊢ ϕ ⊑ ϕ. We now give some other derived refinements.

Lemma 4.3.13 The following rules can be derived:

1. \begin{matrix}Γ, x : ϕ ⊢ P′ ⊃ P & Γ ⊢ ϕ ⊑ ϕ′ \\ Γ ⊢ (x : ϕ)P ⊑ (x : ϕ′)P′ \end{matrix}
2. {Γ, P ⊢ ϕ wf \atop Γ, P ⊢ (x : ϕ)P = ϕ}
3. {Γ ⊢ ϕ wf \atop Γ ⊢ (x : ϕ)⊤ = ϕ}

4.

Γ ⊢ ϕ wf Γ, x : ϕ ⊢ ψ wf Γ, x : ϕ, y : ψ ⊢ Q wf

Γ ⊢ Π_{x:(x:ϕ)P} (y : ψ)Q ⊑ (f : Π_{x:ϕ}ψ) ∀x : ϕ.P ⊃ Q[fx/y]

5.

Γ ⊢ ϕ wf Γ, x : ϕ ⊢ ψ wf Γ, x : ϕ, y : ψ ⊢ Q wf
Γ ⊢ Π_{x:ϕ}(y : ψ)Q = (f : Π_{x:ϕ}ψ) ∀x : ϕ.Q[fx/y]

6.

Γ ⊢ ϕ wf Γ, x : ϕ ⊢ P wf Γ, x : ϕ ⊢ ψ wf Γ, x : ϕ, y : ψ ⊢ Q wf
Γ ⊢ Σ_{x:(x:ϕ)P} (y : ψ)Q = (z : Σ_{x:ϕ}ψ) P [π_{1}z/x] ∧ Q[π_{1}z/x, π_{2}z/y]

7.

Γ ⊢ ϕ wf Γ, y : ϕ ⊢ P wf Γ, x : ϕ, P [x/y] ⊢ Q wf
Γ ⊢ (x : (y : ϕ)P)Q = (z : ϕ)P [z/y] ∧ Q[z/x]

In Remark 4.3.4 we said that refinement types correspond to a relation over a type rather than a subset. One consequence of this is that we cannot give a canonical form for refinement types simply using a type and a proposition as (x : τ)P. This is in contrast to in [NPS90], where subset types are given meaning via a translation into the underlying basic type theory (see Section 5.4.3).

Instead, we introduce the notion of pseudotype, which we will use in the proof of completeness. These have some of the properties of types. For example, pseudotypes have no logical import, in the sense that they are inhabited if and only if the underlying type is. Moreover, we can express all refinement types in a canonical form as a propositional ‘subset’ of pseudotypes.

Definition 4.3.14 The pseudotypes for a given λ_{(:)}-signature are given by the grammar:

κ ::= 1 | γ | Σ_{x:κ}κ′ | Π_{x:ϕ}κ

To see that we must keep the dependent constructors consider, for example, the refinement type Σ_{n:nat}{n}_{nat} → nat. This cannot be expressed in the form ϕ × ϕ′ for any ϕ and ϕ′.

Lemma 4.3.15 For all Γ ⊢ ϕ wf, there exists a pseudotype κ and proposition P such that, Γ ⊢ ϕ = (x : κ)P.

Proof: Use the rules in Lemma 4.3.13.

Lemma 4.3.16 If ⊥, Γ ⊢ ϕ, ϕ′ : Ref (τ), then ⊥, Γ ⊢ ϕ ⊑_{τ} ϕ′.

Proof: By Lemma 4.3.15, we can assume, without loss of generality, that ϕ and ϕ′ are in canonical form. The proof is a straightforward induction over τ. The evident generalisation to arbitrary propositions (if ⊥, Γ ⊢ P wf then ⊥, Γ ⊢ P) holds also.

Well-formedness of terms is a combination of typing and satisfying logical properties. This is illustrated in the following proposition.

Proposition 4.3.17 Given preterm t in context Γ, the following are equivalent:

1. Typify(t) is well-typed in typing context Typify(Γ), where Typify replaces each occurrence of a refinement type with its underlying type and removes propositions from the context.

2. There exists a refinement type ϕ such that ⊥, Γ ⊢ t : ϕ.

3. There exists a type τ, such that for all ⊥, Γ ⊢ ϕ : Ref (τ), we can prove ⊥, Γ ⊢ t : ϕ.

Proof: Clearly (3) implies (2) and, by Lemma 4.3.16, (2) implies (3).

We induct over preterms to show that for each Γ ⊢ t, (1) is equivalent to (2,3). We write \overline{U} for Typify(U). We just consider two cases.

(applications) If ⊥, Γ ⊢ tt′ : ϕ then there exists a ψ such that ⊥, Γ ⊢ t : ψ → ϕ and ⊥, Γ ⊢ t : ψ, so by induction, \overline{Γ} ⊢ \overline{t} : σ → τ and \overline{Γ} ⊢ \overline{t′} : σ for some σ

and τ, and so \overline{Γ} ⊢ \overline{tt′} : τ.

Conversely, if \overline{Γ} ⊢ \overline{tt′} : τ, then \overline{Γ} ⊢ \overline{t} : σ → τ and \overline{Γ} ⊢ \overline{t′} : σ so, by induction,

⊥, Γ ⊢ t : ϕ → ψ and ⊥, Γ ⊢ t′ : ϕ, and so ⊥, Γ ⊢ tt′ : ψ.

(abstractions) If ⊥, Γ ⊢ λx : ϕ.t : χ then ⊥, Γ, x : ϕ, P ⊢ t : χ′ for some P and χ′, so \overline{Γ}, x : σ ⊢ t : τ and then \overline{Γ} ⊢ λx : σ.\overline{t} : σ → τ, that is, \overline{Γ} ⊢ \overline{λx : ϕ.t} :

σ → τ.

Conversely, if \overline{Γ} ⊢ \overline{λx : ϕ.t} : σ → τ, then \overline{Γ}, x : σ ⊢ \overline{t} : τ so, by induction,

⊥, Γ, x : ϕ ⊢ t : ψ for some ψ, and so, ⊥, Γ ⊢ λx : ϕ.t : Π_{x:ϕ}ψ.

Definition 4.3.18 Let Γ ⊢ t be a preterm in context. If the conditions of Proposition 4.3.17 hold, then we say that Γ ⊢ t is well-structured.

We extend the definition of well-structuredness to arbitrary pre-expressions, and write Γ ⊢ U ws for ⊥, Γ ⊢ U wf.

We will sometimes say, informally, that an expression U is well-structured. Note that if Γ is well-formed, then Γ ⊢ U wf iff Γ, ⊥ ⊢ U wf.

There are two levels of ‘well-formedness’ therefore. What we have called well-structured corresponds to terms being put together correctly, irrespective of logical annotation, whereas being well-formed, as such, means that the logic is respected. In contrast to well-formedness, Proposition 4.3.17 shows that wellstructuredness is decidable.

This distinction is reminiscent of the rough types of Sannella and Aspinall, which are like the type underlying a refinement type.

We will see in Section 4.5 that we only give a semantic interpretation to wellstructured terms.

Proposition 4.3.19 If Γ ⊢ ϕ, ϕ′ : Ref (τ), then Γ ⊢ ϕ ⊑ (x : ϕ′) ⊥.

Proof: Since Typify(Γ), x : τ ⊢ x : τ, by Proposition 4.3.17 we infer that Γ, x : ϕ′, ⊥ ⊢ x : ϕ, and so Refinement Types (R) gives Γ ⊢ ϕ ⊑ (x : ϕ′) ⊥.

Remark 4.3.20 Refinement is a definitional extension of refinement typing in the sense that Γ, x : ϕ′ ⊢ x : ϕ if and only if Γ ⊢ ϕ ⊑ ϕ′.

{Γ, x : ϕ′ ⊢ x : ϕ \atop Γ, x : ϕ′, ⊤ ⊢ x : ϕ} ; Γ ⊢ ϕ ⊑ (x : ϕ′)⊤ ^{Ref.Types (R)} \begin{matrix}Γ ⊢ ϕ′ ⊑ ϕ′ & Γ, x : ϕ′ ⊢ ⊤ \\ Γ ⊢ (x : ϕ′)⊤ ⊑ ϕ′ \end{matrix} ^{Ref.Types (L)}
Γ ⊢ ϕ ⊑ ϕ′

In fact, this can be strengthened by showing that for all Γ ⊢ ϕ wf, we can prove Γ ⊢ ϕ ⊑ ϕ′ ≡ ∀x, y : ϕ′ . x =_{ϕ′} y ⊃ x =_{ϕ} y.

A natural question, then, is can we eliminate the refinement relation and treat it as syntactic sugar? As the calculus stands, the Weakening rule for refinement typing has a refinement as hypothesis and so we cannot naively treat ϕ ⊑ ϕ′ as syntactic sugar for Γ, x : ϕ′ ⊢ x : ϕ.

Although the system could be reformulated, we believe it is more insightful to have an explicit definition of refinement (as in Chapter 3). In practice, when applying the rules of Figure 4.9 backwards to find a proof of ϕ ⊑ ϕ′, the refinement type rules are only used in Refinement Types (R), when ϕ′ is of the form (x : ψ)Q.

Remark 4.3.21 Although we have emphasised the per intuition for the λ_{(:)}- calculus, the match is not perfect. For example, we might expect equality at (even → nat) → nat to mean that if arguments are equal at even → nat then the results are equal (at nat), but this is not so. For example, λf : nat → nat . 3 does not equal λf : even → nat . 3 at (even → nat) → nat since the term λf : nat → nat . 3 does not have the refinement type (even → nat) → nat.

In Section 4.5, we will define relations on terms, ≃_{ϕ}, such that these two terms are related by ≃_{(even→nat)→nat}. Then we can think of terms of the calculus as uniquely specifying total terms up to ≃_{ϕ} for some refinement type ϕ.

However, this is more a mismatch of refinement typing than equality, for if t, t′ : ϕ, then t ≃_{ϕ} t′ implies t =_{ϕ} t′.

Remark 4.3.22 We can define a form of annotation, P → ϕ, for proposition, P, and refinement type, ϕ. We will use this notion in the completeness proof below.

The definition is:

\begin{aligned}P → 1 & ≡ & 1 \\ P → γ & ≡ & γ \\ P → Σ_{x:ϕ}ψ & ≡ & Σ_{x:(P →ϕ)}(P → ψ) \\ P → Π_{x:ϕ}ψ & ≡ & Π_{x:(P →ϕ)}(P → ψ) \\ P → (x : ϕ)Q & ≡ & (x : (P → ϕ))P ⊃ Q \end{aligned}

We state the following two properties, for each P and ϕ:

1. If Γ, P ⊢ ϕ wf then Γ ⊢ (P → ϕ) wf.

2. Γ, P ⊢ ϕ = (P → ϕ)

4.4 Division by 2 Revisited

As an illustration of how refinement types can provide a useful proof technique, we give the division by 2 example from Section 4.2 again. Recall that we define iteration from the more general recursion, as

                natiter t t′ n = natrec t (λx : nat.t′) n

where x ∉ F V (t′).

The program is

                     div2 = λn : nat.π_{1}(div2′ n)

div2′ = natiter ⟨0, 0⟩ (λp : nat × nat.⟨π_{2}p, π_{1}p + 1⟩)

We prove that it satisfies the specification

div2 : Π_{n:nat}(m : nat)n = 2m ∨ n = 2m + 1
div2′ : Π_{n:nat}Σ_{(m:nat)n=2m∨n=2m+1}(m′ : nat)m + m′ = n

In fact, there is little of interest in the main part of the proof. Since refinement types explicitly indicate the structure of the specification, this enables much of the proof to be carried out in a syntax-directed fashion. This would be useful for automation.

Write ϕ[n] as an abbreviation for Σ_{m:(m:nat)n=2m∨n=2m+1}(m′ : nat)m + m′ = n.

⟨0, 0⟩ : ϕ[0]

{see below \atop n : nat, p : ϕ[n] ⊢ ⟨π_{2}p, π_{1}p + 1⟩ : ϕ[n + 1]} n : nat ⊢ nat × nat ⊑ ϕ[n]
n : nat ⊢ λp : nat × nat.⟨π_{2}p, π_{1}p + 1⟩ : ϕ[n] → ϕ[n + 1]

n : nat ⊢ natiter ⟨0, 0⟩ (λp : nat × nat.⟨π_{2}p, π_{1}p + 1⟩) n :

Σ_{(m:nat)n=2m∨n=2m+1}(m′ : nat)m + m′ = n

λn : nat.natiter ⟨0, 0⟩ (λp : nat × nat.⟨π_{2}p, π_{1}p + 1⟩) n : Π_{n:nat}Σ_{(m:nat)n=2m∨n=2m+1}(m′ : nat)m + m′ = n The proof continues with

{n : nat, n = 2π_{1}p ∨ n = 2π_{1}p + 1, π_{1}p + π_{2}p = n \atop ⊢ n + 1 = 2π_{2}p ∨ n + 1 = 2π_{2}p + 1} ; n : nat, p : ϕ[n] ⊢ n + 1 = 2π_{2}p ∨ n + 1 = 2π_{2}p + 1

n : nat, p : ϕ[n] ⊢ π_{2}p : (m : nat)n + 1 = 2m ∨ n + 1 = 2m + 1

The remainder of the proof is arithmetic reasoning. In practice, we would use a theorem prover here.

{n : nat, p : ϕ[n] ⊢ π_{1}p + π_{2}p = n \atop n : nat, p : ϕ[n] ⊢ π_{2}p + π_{1}p + 1 = n + 1}

4.5 Models

In contrast with the previous two chapters we will not interpret derivations of judgements, but rather ‘pre-judgements’. This is because we do not have unique refinement typings, or even unique derivations of particular refinement typings. To show that a semantics based on derivations gives unique interpretations would require an analysis of coherence which we avoid. This is not quite what we might call a ‘Curry-style’ interpretation, however, since we do not erase the refinement types from terms.

In Chapter 2, we used Henkin models to interpret the simply-typed lambda calculus and first-order logic. Here we will extend this, and interpret terms as sets (their ‘total realizers’). Refinement types over type σ are interpreted as pers over σ^{A}.

Definition 4.5.1 A Henkin interpretation of a λ_{(:)}-signature is the same as a Henkin interpretation of a first-order λ^{×→}-signature.

Although the raw data of λ_{(:)}- and first-order λ^{×→}-interpretations (Definition 2.4.1) are the same, the induced semantics are different.

We assume some Henkin interpretation below when we write [[ ]]. A Henkin interpretation A models an axiom system when all constants and predicates are given an interpretation, and each axiom is true in the interpretation, as defined below. Although the environment model condition is only given for λ^{×→}, the interpretation of λ_{(:)}-terms is well-defined.

Strictly speaking, the meaning function is a partial mapping from pre-expressions to meanings. We will show later that it is total for the well-structured terms.

a [[Γ ⊢ 1]](η) a′ ⟺ a, a′ ∈ 1^{A}
[[Γ ⊢ ϕ]] = R [[Γ, x : ϕ ⊢ ψ]] = S

a [[Γ ⊢ Σ_{x:ϕ}ψ]](η) a′ ⟺ Proj_{1}^{σ,τ}(a) R(η) Proj_{1}^{σ,τ}(a′) and Proj_{2}^{σ,τ}(a) S⟨η, Proj_{1}^{σ,τ}(a)⟩ Proj_{2}^{σ,τ}(a′)

[[Γ ⊢ ϕ]] = R [[Γ, x : ϕ ⊢ ψ]] = S

f [[Γ ⊢ Π_{x:ϕ}ψ]](η) f′ ⟺ for all a R(η) a′, App(f, a) S⟨η, a⟩ App(f′, a′)

\begin{matrix}[[Γ ⊢ ϕ]] = R & [[Γ, x : ϕ ⊢ P]] = A \\ a [[Γ ⊢ (x : ϕ)P]](η) a′ ⟺ a R(η) a′, ⟨η, a⟩ ∈ A, ⟨η, a′⟩ ∈ A \end{matrix}

a [[Γ ⊢ γ]](η) a′ ⟺ a, a′ ∈ γ^{A} and a = a′

Figure 4.13: Interpretation of Refinement Types

Now, expressions are all interpreted in context, so for context Γ we must first define Γ-environments, η, in interpretation A, written η ⊨^{A} Γ (dropping the A when not significant), where η is a tuple of elements in the domains of the pers for the refinement types in Γ. We define this recursively with the interpretation of refinement types and propositions. For per R, we write a ∈ R to indicate that a is in the domain of R, i.e. a R a.

We first define the notion of equality of environments, in the obvious way, as simultaneous equality of elements in the corresponding per, written η [[Γ]] η′.

⟨⟩ [[⟨⟩]] ⟨⟩
⟨η, a⟩ [[Γ, x : ϕ]] ⟨η′, a′⟩ when η [[Γ]] η′ and a [[Γ ⊢ ϕ]](η) a′
η [[Γ, P]] η′ when η [[Γ]] η′ and η, η′ ∈ [[Γ ⊢ P]]

Then we define η ⊨ Γ to mean η [[Γ]] η.

To avoid questions of coherence, we interpret pre-terms, and so pre-propositions and pre-refinement types too.

Now as mentioned above, refinement types are interpreted as pers. The interpretation is given in Figure 4.13 where we adopt the convention that the pers are over the set corresponding to the underlying type. The unit and ground types are interpreted as identities; the product and function refinement types are interpreted as the expected combination of pers, and (x : ϕ)P is interpreted as the restriction of ϕ to the elements for which P holds. It is easy to see that all types are interpreted as identities.

There is an apparent asymmetry in the definition of the product per for Σ_{x:ϕ}ψ, but in fact, if ϕ is a well-formed refinement type in context Γ, then the soundness result below states that if η [[Γ]] η′ we have [[Γ ⊢ ϕ]](η) = [[Γ ⊢ ϕ]](η′).

In Figure 4.14, The pre-term in context Γ ⊢ t is interpreted in environment g ⊨ Γ as a subset (its ‘total realizers’) of σ^{A}, where σ is the type underlying t. The types are implicit in the interpretation. An alternative would be to make them explicit by giving an interpretation over well-structuredness judgements. This is the approach taken in [Asp97], for example.

{[[Γ ⊢ ϕ]] = R \atop [[Γ, x : ϕ, Γ′ ⊢ x]]⟨η, a, η′⟩ = \{a′ | a′ R(η) a\}}

{[[Γ ⊢ t_{1}]] = m_{1} ⋯ [[Γ ⊢ t_{n}]] = m_{n} \atop [[Γ ⊢ k(t_{1}, …, t_{n})]](η) = \{k^{A}(a_{1}, …, a_{n}) | a_{i} ∈ m_{i}(η)\}}

[[Γ ⊢ ∗]](η) = 1^{A}

[[Γ ⊢ t]] = m [[Γ ⊢ t′]] = m′

[[Γ ⊢ ⟨t, t′⟩]](η) = {a ∈ (σ × τ)^{A} | Proj_{1}^{σ,τ}(a) ∈ m(η), Proj_{2}^{σ,τ}(a) ∈ m′(η)}

[[Γ, x : ϕ ⊢ t]] = m

[[Γ ⊢ λx : ϕ.t]](η) = {f ∈ (σ → τ)^{A} | for all a ∈ [[Γ ⊢ ϕ]](η) . App(f, a) ∈ m⟨η, a⟩}

{[[Γ ⊢ t]] = m \atop [[Γ ⊢ π_{1}(t)]](η) = \{Proj_{1}^{σ,τ}(a) | a ∈ m(η)\}}

{[[Γ ⊢ t]] = m \atop [[Γ ⊢ π_{2}(t)]](η) = \{Proj_{2}^{σ,τ}(a) | a ∈ m(η)\}}

\begin{matrix}[[Γ ⊢ t]] = m & [[Γ ⊢ t′]] = m′ \\ [[Γ ⊢ tt′]](η) = \{App(f, a) | f ∈ m(η), a ∈ m′(η)\} \end{matrix}

Figure 4.14: Interpretation of Terms

It is because of the refinement type in abstractions that we interpret terms as sets rather than as single elements. For example, λn : even.n is interpreted as the set of elements in (nat → nat)^{A} which are the identity for even arguments. In Figure 4.15 we give the interpretation of propositions. We interpret a preproposition in context Γ ⊢ P as the set of environments η ⊨ Γ in which P holds.

As we mentioned above, although the interpretation function is partial, wellstructured terms are always given an interpretation. For example, (λn : even.n)∗ does not have a well-defined interpretation, but (λn : even.n)3 does, even though it is not syntactically well-formed.

Proposition 4.5.2 If Γ ⊢ U ws and η ⊨ Γ, then [[Γ ⊢ U]](η) is defined.

Proof: We induct over pre-expressions, and consider two cases.

(applications) If Γ ⊢ tt′ ws, then \overline{Γ} ⊢ \overline{tt′} : τ, so \overline{Γ} ⊢ \overline{t} : σ → τ and \overline{Γ} ⊢ \overline{t′} : σ, so by the inductive hypothesis, [[Γ ⊢ t]](η) is a well-defined subset of (σ → τ)^{A}, and [[Γ ⊢ t′]](η) is a well-defined subset of σ^{A}. Hence, [[Γ ⊢ tt′]](η) is a well-defined subset of τ^{A}.

(abstractions) If Γ ⊢ λx : ϕ.t ws then \overline{Γ} ⊢ λx : σ.\overline{t} : σ → τ, so \overline{Γ}, x : σ ⊢ \overline{t} : τ and, by induction, if η ⊨ Γ and a ∈ [[Γ ⊢ ϕ]](η), then [[Γ, x : ϕ ⊢ t]](⟨η, a⟩) ⊆ τ^{A} is well-defined.

[[Γ ⊢ ⊥]] = ∅

[[Γ ⊢ P ⊃ P′]] = {η ⊨ Γ | η ∉ [[Γ ⊢ P]] or η ∈ [[Γ ⊢ P′]]} [[Γ ⊢ ∀x : ϕ.P]] = {η ⊨ Γ | ∀a ∈ [[Γ ⊢ ϕ]](η) . ⟨η, a⟩ ∈ [[Γ, x : ϕ ⊢ P]]}

{[[Γ ⊢ t_{1}]] = m_{1} ⋯ [[Γ ⊢ t_{n}]] = m_{n} \atop [[Γ ⊢ F (t_{1}, …, t_{n})]] = \{η ⊨ Γ | ∀a_{i} ∈ m_{i}η . ⟨a_{1}, …, a_{n}⟩ ∈ F^{A}\}}

\begin{matrix}[[Γ ⊢ t]] = m & [[Γ ⊢ t′]] = m′ & [[Γ ⊢ ϕ]] = R \\ [[Γ ⊢ t =_{ϕ} t′]] = \{η ⊨ Γ | ∀a ∈ m(η) . ∀a′ ∈ m′(η) . a R(η) a′\} \end{matrix}

\begin{matrix}[[Γ ⊢ ϕ]] = R & [[Γ ⊢ ϕ′]] = R′ \\ [[Γ ⊢ ϕ ⊑ ϕ′]] = \{η ⊨ Γ | R(η) ⊇ R′(η)\} \end{matrix}

Figure 4.15: Interpretation of Propositions

We may now say what the semantic analogues of the judgements are. Let A be a λ_{(:)}-Henkin interpretation, and assume that η ⊨^{A} Γ. Define Γ ⊨^{A,η} t : ϕ when for all η [[Γ]] η′, for all a ∈ [[Γ ⊢ t]](η) and a′ ∈ [[Γ ⊢ t]](η′), we have a [[Γ ⊢ ϕ]](η) a′. In other words, the interpretation is unique up to the equality of the per. We say that Γ ⊨^{A,η} P when η ∈ [[Γ ⊢ P]]. In particular, the refinement Γ ⊨^{A,η} ϕ ⊑ ϕ′ is true when there is an inclusion of pers [[Γ ⊢ ϕ′]](η) ⊆ [[Γ ⊢ ϕ]](η). We define Γ ⊨^{A,η} ϕ wf to mean: for all η [[Γ]] η′, [[Γ ⊢ ϕ]](η) = [[Γ ⊢ ϕ]](η′), and Γ ⊨^{A,η} P wf to mean: for all η [[Γ]] η′, η ∈ [[Γ ⊢ P]] ⟺ η′ ∈ [[Γ ⊢ P]]. We define validity of a basic judgement, B, to be its truth in all environments, that is, Γ ⊨^{A} B means: for all η ⊨^{A} Γ, Γ ⊨^{A,η} B.

In defining when an interpretation models an axiom system we only require the well-formed axioms to hold.

Definition 4.5.3 Let ⟨Sg, Ax⟩ be a λ_{(:)}-axiom system. A Henkin interpretation A of Sg is a model of ⟨Sg, Ax⟩ when

• for each Γ ⊢ P ∈ Ax, if ⟨Sg, Ax⟩ ▷ Γ ⊢ P wf then Γ ⊨^{A} P.

• for each Γ ⊢ k : ϕ_{1}, …, ϕ_{n} → ψ ∈ Ax such that Sg ▷ k : σ_{1}, …, σ_{n} → τ, ⟨Sg, Ax⟩ ▷ Γ ⊢ ϕ_{i} : Ref (σ_{i}), ⟨Sg, Ax⟩ ▷ Γ ⊢ ψ : Ref (τ), for all η ⊨^{A} Γ, if a_{i} [[Γ ⊢ ϕ_{i}]]^{A}(η) a′_{i} for each i = 1, …, n, then

k^{A}(a_{1}, …, a_{n}) [[Γ ⊢ ψ]]^{A}(η) k^{A}(a′_{1}, …, a′_{n})

We write this as Γ ⊨^{A} k : ϕ_{1}, …, ϕ_{n} → ψ.

First we give a substitution lemma.

Lemma 4.5.4 (Substitution Lemma) If Γ ⊨^{A,η} t_{i} : ϕ_{i} (i = 1, …, n), then

[[x_{1} : ϕ_{1}, …, x_{n} : ϕ_{n} ⊢ U]]^{A} ⟨a_{1}, …, a_{n}⟩ = [[Γ ⊢ U[t_{i}/x_{i}]]]^{A}(η)

where a_{i} ∈ [[Γ ⊢ t_{i}]]^{A}(η) (so ⟨a_{1}, …, a_{n}⟩ ⊨^{A} x_{1} : ϕ_{1}, …, x_{n} : ϕ_{n}).

Proof: Induction over x_{1} : ϕ_{1}, …, x_{n} : ϕ_{n} ⊢ U.

We need the condition that Γ ⊨^{A,η} t_{i} : ϕ_{i}. The weaker requirement that for a_{i} ∈ [[Γ ⊢ t_{i}]]^{A}(η), a_{i} ∈ [[Γ ⊢ ϕ_{i}]]^{A}(η) is not sufficient. For example, in any model λn : even.n ⊭ nat → nat, and for each a ∈ [[λn : even.n]] we have a ⊨ nat → nat, but [[f : nat → nat ⊢ f]](a) ≠ [[λn : even.n]].

Note also that the substitution pre-expression Γ ⊢ U[t_{i}/x_{i}] might not be wellformed. It is, however, if Γ ⊢ t_{i} : ϕ_{i} (for i = 1, n).

A consequence of the substitution lemma is that for x : ϕ ⊢ U wf, and Γ ⊨^{A,η} t : ϕ, we can unambiguously use the notation [[x : ϕ ⊢ U]] ([[Γ ⊢ t]](η)) (dropping the A) to mean [[x : ϕ ⊢ U]](a) for any a ∈ [[Γ ⊢ t]](η). We can then express (an instance of) Lemma 4.5.4 as

[[x : ϕ ⊢ U]] ([[Γ ⊢ t]](η)) = [[Γ ⊢ U[t/x]]](η)

More generally,

[[Γ, x : ϕ, Γ′ ⊢ U]]⟨η, [[Γ ⊢ t]](η), η′⟩ = [[Γ, Γ′[t/x] ⊢ U[t/x]]]⟨η, η′⟩

Lemma 4.5.5 If ⊨ t =_{ϕ} t′ and x : ϕ ⊨ U wf, then [[U[t/x]]] = [[U[t′/x]]].

Proof: Let a ∈ [[t]], a′ ∈ [[t′]]. Then a [[ϕ]] a′, and [[U[t/x]]] = [[x : ϕ ⊢ U]](a) = [[x : ϕ ⊢ U]](a′) = [[U[t′/x]]].

Lemma 4.5.6 For ⊨ t : ϕ, and x : ϕ ⊨ P wf, if [[t]] ⊆ [[x : ϕ ⊢ P]] then ⊨ P [t].

Proof: This is an immediate consequence of Lemma 4.5.4.

We now verify that interpretations respect the rules of the calculus, that is, the calculus is sound with respect to models of λ_{(:)}-axiom systems. A consequence of this is that the axiom system for booleans and naturals is consistent since we can give nontrivial models.

Theorem 4.5.7 (Soundness) Let A be a Henkin model of λ_{(:)}-axiom system ⟨Sg, Ax⟩. Then if ⟨Sg, Ax⟩ ▷ Γ ⊢ t : ϕ then Γ ⊨^{A} t : ϕ, if ⟨Sg, Ax⟩ ▷ Γ ⊢ ϕ wf then Γ ⊨^{A} ϕ wf, if ⟨Sg, Ax⟩ ▷ Γ ⊢ P wf then Γ ⊨^{A} P wf, and if ⟨Sg, Ax⟩ ▷ Γ ⊢ P then Γ ⊨^{A} P.

Proof: Simultaneous induction over all derivations. The soundness of β-equality for abstractions follows from Lemma 4.5.4. Assume that Γ, x : ϕ ⊨ t : ψ, Γ ⊨ t′ : ϕ, and let η [[Γ]] η′, a ∈ [[Γ ⊢ (λx : ϕ.t)t′]](η), a′ ∈ [[Γ ⊢ t[t′/x]]](η′). Then a [[Γ ⊢ ψ[t′/x]]](η) a′. Although we interpret a term in an environment as a set, the soundness theorem shows that since contexts can be seen as pers, the interpretation of a term gives rise to a morphism of pers, that is, a map of equivalence classes. For example, we can think of the interpretation of a variable as a map from an element to its equivalence class (in the relevant refinement type). These informal remarks will be further clarified in the next chapter.

A more challenging question is whether the calculus is in any sense complete, that is, if a particular judgement holds in all the models of some axiom system then it is provable. The ‘ideal’ completeness theorem would be (for refinement typings) that if Γ ⊨ t : ϕ then Γ ⊢ t : ϕ. Unfortunately, this fails for two reasons. Firstly, due to the way in which well-formedness is combined with satisfying logical properties, we must assume that the judgement is well-formed, by which we mean the well-formedness of its component expressions. This is because it is possible for non well-formed terms to have a unique interpretation, and so, semantically, have a refinement type. For example, (λn : even . ∗)3 is interpreted as the unique inhabitant of unit type, but cannot be typed in the system.

The second point arises with higher-order terms, and is due to the calculus requiring arguments to an abstraction to have the refinement type on the abstraction, but the model just needing equality of arguments at that refinement type to give equal results. For example, λf : nat → nat . 3 has the refinement type (even → nat) → nat in the model, but not in the calculus.

What we can show, however, is that if a term in context, Γ ⊢ t, has refinement type ϕ in the model, then there exists a term t′ which does have refinement type ϕ, such that Γ ⊨ t =_{ϕ} t′. In other words, t and t′ correspond to the same equivalence class of ϕ. We will give a syntactic characterisation of this.

As in the previous two chapters, we prove completeness using the notion of Henkin theory, suitably extended. We will regard theories as infinite contexts, Γ, rooted on the left, for which Γ ⊢ P iff P ∈ Γ. We say that an infinite context is well-formed when every finite prefix is well-formed.

Definition 4.5.8 Let ⟨Sg, Ax⟩ be a λ_{(:)}-axiom system. A λ_{(:)}-Henkin theory over ⟨Sg, Ax⟩, is a well-formed infinite context, Γ, closed under derivation from ⟨Sg, Ax⟩ such that if ∃x : ϕ.P is in Γ, then there is a term Γ ⊢ t : ϕ such that P [t/x] is in Γ.

The completeness proof rests on the construction of a term model, formed from a suitable Henkin theory.

There are actually a number of possibilities which, a priori, we can consider for the class of terms in the set τ^{A}. Firstly, there is a choice between total terms of λ^{×→} and arbitrary terms of λ_{(:)}. Another choice is between well-formed terms — either terms with type τ, or with any refinement type ϕ such that ϕ : Ref (τ)

— or all well-structured terms over τ.

We rule out total terms at types because, with such an interpretation, it is not immediately obvious how to construct an environment in the proof of completeness. For example, if halt : Ref(nat) is the refinement type of encodings of programs which halt, and x : halt → nat is in the context, then there would be no term in nat^{A} (i.e. no term of type nat) which equals x.

We do not use arbitrary terms at refinement types because, as pointed out in the discussion after Lemma 4.5.4, this would lead to substitutions not being well-formed. For example,

[[h : (even → nat) → nat ⊢ (λh′ : (even → nat) → nat.h′)h]] [λf : nat → nat.3]

would contain a pre-term which is not well-formed.

Thus, we will use the well-structured terms of λ^{×→}. We use the following definition to characterise the term model.

Definition 4.5.9 We define P̃ for each proposition, P by replacing each equality, t =_{ϕ} t′, with t ≃_{ϕ} t′, where we write:

• t_{1} ≃_{1} t_{2} for t_{1} =_{1} t_{2} • t_{1} ≃_{γ} t_{2} for t_{1} =_{γ} t_{2}

• t_{1} ≃_{Σ_{x:ϕ}ψ} t_{2} for π_{1}(t_{1}) ≃_{ϕ} π_{1}(t_{2}) ∧ π_{2}(t_{1}) ≃_{ψ[π_{1}(t_{1})]} π_{2}(t_{2})

• t_{1} ≃_{Π_{x:ϕ}ψ} t_{2} for ∀x : τ.∀x′ : τ . x ≃_{ϕ} x′ ⊃ t_{1}x ≃_{ψ} t_{2}x′ (where ϕ : Ref (τ))

• t_{1} ≃_{(x:ϕ)P} t_{2} for t_{1} ≃_{ϕ} t_{2} ∧ P̃[t_{1}] ∧ P̃[t_{2}]

Lemma 4.5.10 If Γ ⊢ t ≃_{ϕ′} t′ and Γ ⊢ ϕ ⊑ ϕ′ then Γ ⊢ t ≃_{ϕ} t′.

Proof: Induction over Γ ⊢ ϕ ⊑ ϕ′.

Lemma 4.5.11 Suppose x ∉ F V (ψ). If Γ, x : ϕ ⊢ t : ψ and Γ ⊢ t_{1} ≃_{ϕ} t_{2}, then Γ ⊢ t[t_{1}/x] ≃_{ψ} t[t_{2}/x].

Lemma 4.5.12 If Γ ⊢ t ≃_{ϕ} t then there exists a t′ such that Γ ⊢ t ≃_{ϕ} t′ and Γ ⊢ t′ : ϕ.

Proof: Induction over ϕ.

Definition 4.5.13 Let u and t be well-formed terms with u total. We define u sat^{Γ} t to mean: for all ϕ, if Γ ⊢ t : ϕ then Γ ⊢ u ≃_{ϕ} t.

Although this definition would make sense for arbitrary well-formed terms, the idea is that it formalises when total u is a realizer of t. We superscript the context, Γ, rather than writing Γ ⊢ u sat t, so that when we use infinite contexts, Γ_{∞}, this will not clash with our convention of writing Γ_{∞} ⊢ B to mean Γ ⊢ B for Γ ‘some sub-context’ of Γ_{∞}.

Definition 4.5.14 We define Γ ⊢ t ∼ ϕ to mean: there exists a term t′ such that Γ ⊢ t′ : ϕ and Γ ⊢ t ≃_{ϕ} t′.

In Definition 2.4.4, we defined the Henkin closure of a collection of first-order λ^{×→}-propositions by adding variables for every nonempty type and propositions stating that all existentials have witnesses. The analogous definition here would be to repeat this for all refinement types (over nonempty types), ϕ, and propositions, ∃x : ϕ.P, but there are several problems with this.

One problem is that we cannot just assume some variable x : ϕ, because this has some logical import which might give a contradiction in the current context. We will see that it suffices, in fact, to work with pseudotypes (Definition 4.3.14), since for pseudotype, κ, the assumption x : κ has no logical import.

Another problem is that an arbitrary (well-structured) proposition or pseudotype need not be well-formed in the given context. An added complication is that the order in which assumptions are listed might be significant. We can get round these problems by the following trick. For any well-structured expression, U, we can give a well-formed proposition, wf(U), which says that U is well-formed. Then, for example, the proposition wf(P) ⊃ P is always well-formed, and when P is actually well-formed, is equivalent to P. Similarly, for any well-structured pre-refinement type, ϕ, the refinement type wf(ϕ) → ϕ (using the notation of Remark 4.3.22) is always well-formed.

Thus, the only order that matters in the Henkin theory is that variables precede any expressions in which they appear. This is similar to Definition 2.4.4 for first-order λ^{×→}.

Definition 4.5.15 Let Γ be a λ_{(:)}-context. We define the Henkin closure of Γ by the following procedure.

1. Iterate through the well-structured κ deciding which are inhabited (as in Definition 2.4.4),

2. List all well-structured propositions of the form ∃x : κ_{n}.P_{n} for inhabited κ_{n}.

3. Make a list of variables {y_{n} : κ_{n}} such that y_{n} ∉ P_{n′}, κ_{n′}, for n′ ≤ n, and F V (∃x : κ_{n}.P_{n}) ⊆ Γ, y_{1} : κ_{1}, …, y_{n} : κ_{n}.

4. Let Q_{n} ≡ wf(∃x : κ_{n}.P_{n}) ⊃ ∃x : κ_{n}.P_{n} ⊃ P_{n}[y_{n}/x], and κ′_{n} ≡ wf(κ_{n}) → κ_{n}. Define the Henkin closure, Γ_{H}, as Γ, y_{1} : κ′_{1}, Q_{1}, y_{2} : κ′_{2}, Q_{2}, …

As was the case with first-order λ^{×→} and λ_{?}, although we do not have minimal term models (due to having propositional assumptions), we can still use a term model construction to prove completeness. We use a slight generalisation of the standard ‘consistency implies satisfiability’ argument.

First we generalise consistency and satisfiability from sets of closed propositions to arbitrary contexts. We say that context Γ is consistent, when Γ ⊬ ⊥, and satisfiable, when there exists a model A and Γ-environment η in A, that is, η ⊨^{A} Γ. In the case that Γ is a context of closed propositions, these reduce to the usual definitions of consistency and satisfiability. Let us write Γ ⊨ P to mean, informally, that Γ ⊨^{A} P for every model A of some axiom system. Now we want to show that Γ ⊨ P ⇒ Γ ⊢ P, so suppose Γ ⊬ P. Then Γ, ¬P is consistent and so, by assumption, is satisfiable. Hence Γ ⊭ P. The situation for the other judgement form, namely refinement typings, can be reduced to that of propositions.

Theorem 4.5.16 (Completeness) Let ⟨Sg, Ax⟩ be a λ_{(:)}-axiom system. For ⟨Sg, Ax⟩ ▷ Γ ⊢ P wf, if Γ ⊨^{A} P for all Henkin models A of ⟨Sg, Ax⟩ then ⟨Sg, Ax⟩ ▷ Γ ⊢ P. For ⟨Sg, Ax⟩ ▷ Γ ⊢ t wf and ⟨Sg, Ax⟩ ▷ Γ ⊢ ϕ wf, if Γ ⊨^{A} t : ϕ for all models A of ⟨Sg, Ax⟩ then ⟨Sg, Ax⟩ ▷ Γ ⊢ t ∼ ϕ.

Proof:

Let Γ be a λ_{(:)}-context such that Γ̃ is consistent. First we sketch the construction of a particular model T and environment η such that η ⊨^{T} Γ (steps 1-5), and then use this to deduce completeness (step 6).

1. Construct a maximal consistent Henkin theory Γ_{∞} such that {P | Γ̃ ⊢ P } ⊆ Γ_{∞}. Let Γ_{H} be the Henkin closure of { \atop Γ ∪ Ax}. We apply the ˜( ) operation to the axioms so that the interpretation will be a model. First we consider sets of propositions, Δ, with the property that Δ can be ‘inserted’ into Γ_{H} giving a consistent extension; that is, there exists a context Γ′ such that Γ is a subcontext of Γ′, and Γ′ consists of Γ plus the propositions in Δ, in some order. We form the partial order of such sets, ordered by subsetting. This is clearly nonempty. It is also closed under unions of chains. To see why, let us formalise the insertion of a set, Δ_{n}, as a mapping i_{n} : Δ_{n} → N. Then, by always adding new elements ‘to the right’, it is possible to insert Δ_{n} in Γ_{H} in such a way that all supersets can be inserted in a way which extends this insertion, that is, such that i_{n+1} ↾ Δ_{n} = i_{n}. The limit, i_{∞}, is not necessarily an embedding of the union, since it might try to insert all the set at one place (if ι(n) = n, for example). We must be careful to ‘spread’ the set throughout Γ_{H}. This can be achieved by inserting each element at double the index of the naive embedding so we define i(n) = 2 × i_{∞}(n). Hence each chain has an upper bound and so, by Zorn’s Lemma, the collection has a maximal element, Δ_{∞}. We define Γ_{∞} to be any insertion of Δ_{∞} into Γ_{H}. Clearly Γ_{∞} is a theory, that is, it is well-formed and closed under deduction.

2. We define a relation = on well-structured terms in Γ_{∞}. For Γ_{∞} ⊢ u, u′ ws, we define u = u′ to mean: if Γ_{∞} ⊢ u, u′ wf then u sat^{Γ_{∞}} u′ and u′ sat^{Γ_{∞}} u; otherwise, if neither is well-formed then ⊤, else ⊥. The intuition behind this definition is that terms which are not well-formed correspond to the set of all well-structured terms and so should be equal. Define σ^{T} as the set of =-equivalence classes of well-structured terms of λ^{×→}, open with respect to Γ_{∞}, and over the type σ, that is,

{u | Γ_{∞}, ⊥ ⊢ u : σ}

The position of ⊥ in the context does not matter since Γ_{∞} is well-formed. We write [u] for the equivalence class of u. We construct a Henkin interpretation, T , by interpreting constants syntactically. For constant symbol k : τ_{1}, …, τ_{n} → τ, define k^{T} : τ_{1}^{T} ×⋯×τ_{n}^{T} → τ^{T} as k^{T} ([u_{1}], …, [u_{n}]) = [k(u_{1}, …, u_{n})]. Since these terms are well-structured, the interpretation is well-defined. For predicate symbol F : Pred (τ_{1}, …, τ_{n}), define F^{T} ⊆ τ_{1}^{T} × ⋯ × τ_{n}^{T} as {⟨[u_{1}], …, [u_{n}]⟩ | F (u_{1}, …, u_{n}) ∈ Γ_{∞}}. We can show that T is extensional, and since the environment model condition clearly holds, T is an interpretation.

3. We must characterise the interpretation of terms and refinement types in the term interpretation, T . We need two cases, if η′ ⊨^{T} Γ′:

• for Γ_{∞} ⊢ P̃[η′/Γ′] wf, prove that Γ′ ⊨^{T ,η′} P ⟺ Γ_{∞} ⊢ P̃[η′/Γ′]

• for Γ_{∞} ⊢ ϕ̃[η′/Γ′] wf, [u] [[Γ′ ⊢ ϕ]]^{T} (η′) [u′] ⟺ Γ_{∞} ⊢ u ≃_{ϕ̃[η′/Γ′]} u′

This is carried out in Lemma 4.5.17 below, after the sketch of completeness.

4. The interpretation, T , is a model of the axioms. For each axiom of the form Γ′ ⊢ P, we can define a closed equivalent which we write as ∀Γ′ . P. This has the obvious inductive definition: ∀⟨⟩ . P ≡ P, ∀(Γ′, x : ϕ) . P ≡ ∀Γ′ . ∀x : ϕ.P, and ∀(Γ′, P′) . P ≡ ∀Γ′ . P′ ⊃ P. Then since { \atop ∀Γ′.P} is well-formed and Γ_{∞} ⊢ { \atop ∀Γ′.P}, by the previous step we get ⊨^{T} ∀Γ′.P and so Γ′ ⊨^{T} P. We must show that for each axiom Γ′ ⊢ k : ϕ → ψ (without loss of generality, we just consider unary constants), for all η′ ⊨^{T} Γ′, for all a [[Γ′ ⊢ ϕ]](η) a′, that k^{T} (a) [[Γ′ ⊢ ψ]](η) k^{T} (a′). By step 3, this is if when Γ_{∞} ⊢ η′ ≃_{Γ′} η′ and Γ_{∞} ⊢ u ≃_{ϕ[η′/Γ′]} u′, then Γ_{∞} ⊢ k(u) ≃_{ψ[η′/Γ′]} k(u′).

We use singleton types (as discussed on p. 106) to derive this:

{Γ_{∞} ⊢ u ≃_{ϕ[η′/Γ′]} u′ \atop Γ_{∞}, Γ′, η′ ≃_{Γ′} η′ ⊢ u ≃_{ϕ} u′}

Γ_{∞}, Γ′, η′ ≃_{Γ′} η′ ⊢ k(u) ≃_{ψ} k(u′)

(Lemma 4.5.11)

Γ_{∞} ⊢ k(u) ≃_{ψ[η′/Γ′]} k(u′)

Hence T models ⟨Sg, Ax⟩.

5. As in the proof of completeness for first-order λ^{×→}, if x_{1} : ϕ_{1}, …, x_{n} : ϕ_{n} are the variables in Γ, then we define the Γ-environment, η, as ⟨[x_{1}], …, [x_{n}]⟩. We can show that η ⊨^{T} Γ, by induction over subcontexts of Γ. Thus for an arbitrary context, Γ, if Γ̃ is consistent then Γ is satisfiable.

6. Finally, we show that if Γ ⊨^{T} B then Γ̃ ⊢ B̃, for B a proposition or refinement typing (writing t̃ : ϕ for t ∼ ϕ). Thus if Γ ⊨ B we have Γ̃ ⊢ B̃. Suppose Γ̃ ⊬ P̃. Then Γ̃, ¬P̃ is consistent, so by the previous step, there is an environment, η, such that η ⊨^{T} Γ, ¬P, so Γ ⊭^{T ,η} P, and Γ ⊭ P. The situation for refinement typings can be reduced to that of propositions, since Γ′ ⊨^{T ,η′} t : ϕ is equivalent to Γ′ ⊨^{T ,η′} t =_{ϕ} t. The crucial point is that the permissive well-formedness rule for equalities (Equality) means that t =_{ϕ} t′ is well-formed even though t and t′ need not have refinement type ϕ. Then, Γ ⊨ t =_{ϕ} t implies Γ̃ ⊢ t ≃_{ϕ} t so, by Lemma 4.5.12, Γ̃ ⊢ t ∼ ϕ.

In order to prove the equivalence in step 3, we need to characterise the interpretation of expressions in the term model, T . Because of the mutual recursion between terms, refinement types and propositions, we must carry out the proof for each syntactic category simultaneously.

For the reasons given in Remark 4.3.21, the pers, [[ϕ]]^{T}, do not correspond exactly to the equalities, =_{ϕ}.

Lemma 4.5.17 Let Γ be a λ_{(:)}-context and define Γ_{∞} as in the proof of Theorem 4.5.16. Then, for η ⊨^{T} Γ:

1. For Γ_{∞} ⊢ ϕ̃[η/Γ] wf, [u] [[Γ ⊢ ϕ]]^{T} (η′) [u′] ⟺ Γ_{∞} ⊢ u ≃_{ϕ̃[η/Γ]} u′

2. For Γ_{∞} ⊢ t̃[η/Γ] wf, [[Γ ⊢ t]]^{T} (η) = {[u] | u sat^{Γ_{∞}} t̃[η/Γ]}

3. For Γ_{∞} ⊢ P̃[η/Γ] wf, Γ ⊨^{T ,η} P iff Γ_{∞} ⊢ P̃[η/Γ] Proof: Simultaneous induction over all expressions, unpacking the definition in the term model. The inductive ordering is

P, P′ < P ⊃ P′
ϕ, P [t] < ∀x : ϕ.P
ϕ < t =_{ϕ} t′
t < F (t)
ϕ, ϕ′ < ϕ ⊑ ϕ′
ϕ, ψ[t] < Σ_{x:ϕ}ψ
ϕ, ψ[t] < Π_{x:ϕ}ψ
ϕ, P [t] < (x : ϕ)P

The interesting cases are for propositions so we prove these in detail. To save on symbols, we will write \overline{U} for Ũ[η/Γ].

• Γ ⊭^{T ,η} ⊥, and Γ_{∞} ⊬ ⊥ by construction of Γ_{∞}.

• Γ ⊨^{T ,η} P ⊃ Q ⟺ Γ ⊭^{T ,η} P or Γ ⊨^{T ,η} Q ⟺ Γ_{∞} ⊢ \overline{¬P} or Γ_{∞} ⊢ \overline{Q} ⟺ Γ_{∞} ⊢ \overline{P ⊃ Q}.

• Γ ⊨^{T ,η} ∀x : ϕ.P when for all a ∈ [[Γ ⊢ ϕ]](η), ⟨η, a⟩ ∈ [[Γ, x : ϕ ⊢ P]]. Now if Γ_{∞} ⊢ t : \overline{ϕ}, by the inductive hypothesis on ϕ we have Γ ⊨^{T ,η} t : ϕ, and so for each a ∈ [[Γ ⊢ t]](η), ⟨η, a⟩ ∈ [[Γ, x : ϕ ⊢ P]]. Then, by Lemma 4.5.6, Γ ⊨^{T ,η} P [t/x], and by the inductive hypothesis on P [t/x], Γ_{∞} ⊢ P̄[t/x]. In other words, for all Γ_{∞} ⊢ t : \overline{ϕ} we have Γ_{∞} ⊢ \overline{P} [t/x]. Thus Γ_{∞} ⊢ \overline{∀x : ϕ.P}, for if not, by maximality we would have Γ_{∞} ⊢ \overline{∃x : ϕ.¬P}, and since Γ_{∞} is a Henkin theory, Γ_{∞} ⊢ ¬\overline{P} contradicting the above. Conversely, suppose Γ_{∞} ⊢ \overline{∀x : ϕ.P}. Now let [u] ∈ [[Γ ⊢ ϕ]](η). By the inductive hypothesis and Lemma 4.5.12, Γ_{∞} ⊢ u ∼ \overline{ϕ}, that is, there exists t : \overline{ϕ} such that u ≃\overline{ϕ} t. Now by Quantification, Γ_{∞} ⊢ \overline{P} [t/x] so by the inductive hypothesis, Γ ⊨^{T ,η} P [t/x], so ⟨η, u⟩ ∈ [[Γ ⊢ P]]. Hence Γ ⊨^{T ,η} ∀x : ϕ.P.

• Γ ⊨^{T ,η} F (t) when [[Γ ⊢ t]](η) ⊆ F^{T}. Since t is well-formed we have Γ ⊢ t : ϕ. Define the maximal refinement type over τ, max(τ), as:

max(1) = 1

max(γ) = γ
max(σ × τ) = max(σ) × max(τ)
max(σ → τ) = (x : σ)⊥ → τ

Then all terms over τ have refinement type max(τ). Let χ be max(τ), where ϕ : Ref (τ). Then we have x : χ ⊢ F (x) wf by the permissive wellformedness rule for predications. Let [u] ∈ [[Γ ⊢ t]](η), so by induction on t, u sat^{Γ_{∞}} \overline{t}, and since u : χ, and u =_{χ} t̄, by Elimination Rules we have Γ_{∞} ⊢ F (t̄). Conversely, if Γ_{∞} ⊢ F (\overline{t}), then Γ_{∞} ⊢ t : (x : ϕ)F (x) for some ϕ, so by the definition of sat, Γ_{∞} ⊢ F (u) for all u sat^{Γ_{∞}} \overline{t}, and so [[Γ ⊢ t]](η) ⊆ F^{T}.

• Γ ⊨^{T ,η} t =_{ϕ} t′ is: for all u sat^{Γ_{∞}} \overline{t} and u′ sat^{Γ_{∞}} \overline{t′}, we have Γ_{∞} ⊢ u ≃\overline{ϕ} u′. We can show that this is equivalent to Γ_{∞} ⊢ \overline{t} ≃\overline{ϕ} \overline{t′}, by induction over \overline{ϕ}.

• Assume that Γ ⊨^{T ,η} ϕ ⊑ ϕ′. By induction on ϕ, ϕ′, this is: for all u, u′, if u ≃\overline{ϕ′} u′ then u ≃\overline{ϕ} u′. If x : \overline{ϕ′} ∈ Γ_{∞} then Γ_{∞} ⊢ x : \overline{ϕ} and we can deduce (see Remark 4.3.20) that Γ_{∞} ⊢ \overline{ϕ} ⊑ \overline{ϕ′}. If \overline{ϕ′} ∉ Γ_{∞}, then we can prove Γ_{∞} ⊢ \overline{ϕ} ⊑ \overline{ϕ′} directly. Since \overline{ϕ′} ∉ Γ_{∞} there does not exist a term Γ_{∞} ⊢ t : \overline{ϕ′}. Suppose ∃x : \overline{ϕ′} . ⊤ ∈ Γ_{∞}. Then since Γ_{∞} is a λ_{(:)}-Henkin theory we must have Γ_{∞} ⊢ t′ : \overline{ϕ′}, for some t′. We deduce that there exists a Γ_{∞} ⊢ t′ : \overline{ϕ′}, a contradiction. Hence, since Γ_{∞} is maximally consistent, we must have ¬∃x : \overline{ϕ′} . ⊤ ∈ Γ_{∞}, and so Γ_{∞}, x : \overline{ϕ′} ⊢ ⊥. Hence by Refinement Types (L), Γ_{∞} ⊢ (x : \overline{ϕ′}) ⊥ ⊑ \overline{ϕ′}. Now Proposition 4.3.19 gives us Γ_{∞} ⊢ \overline{ϕ} ⊑ (x : \overline{ϕ′}) ⊥ and so using Transitivity, Γ_{∞} ⊢ \overline{ϕ} ⊑ \overline{ϕ′}. Conversely, suppose Γ_{∞} ⊢ \overline{ϕ} ⊑ \overline{ϕ′}, then by Lemma 4.5.10 and the inductive hypothesis on \overline{ϕ} and \overline{ϕ′}, we get Γ ⊨^{T ,η} \overline{ϕ} ⊑ \overline{ϕ′}.

Now since first-order logic, the first-order logic of simply-typed lambda calculus, and the refinement types calculus are all complete for the class of Henkin models (without the assumption of nonemptiness), we have:

Corollary 4.5.18 The calculus is a conservative extension of the first-order logic of λ^{×→}: If Γ ⊢ t =_{τ} t′ is a well-formed equation in λ^{×→}, then it is provable in λ^{×→}, if and only if it is provable in the calculus of refinement types.

Corollary 4.5.19 The calculus is a conservative extension of first-order logic: If Γ ⊢ P wf does not contain any refinement types, then it is provable in first-order logic, if and only if it is provable in the calculus of refinement types.

The significance of these corollaries is that we are free to use the specification language for proving program equivalences and for reasoning about programs using the program logic, in the knowledge that it faithfully reflects the equality in the underlying programming language, and proofs in the program logic.

4.6 Conclusions

We have described the refinement type methodology of specification. This is a way of combining the type system of a programming language with a program logic to give a specification language. This is an alternative to approaches which rely on encoding a logic into an expressive type theory, and those which simply use a program logic.

Although we give a refinement relation ϕ ⊑ ϕ′ on specifications, this does not constitute a full refinement calculus (such as in [Mor94]). The idea there is to internalise specifications into programs and consider a refinement relation on mixtures of specification and program.

In the proof that div2 satisfied its specification we used the proof for div2′. There is an implicit element of refinement on terms here. This is made explicit in the next chapter.

Refinement Calculus

In this chapter, we present the full refinement calculus, λ_{⊑}. This is a calculus in which the stepwise refinement of logical specifications into programs, and the correctness of partially developed programs can be formalised. The calculus combines the refinement terms and refinement types calculi of the previous two chapters.

We define a notion of refinement axiom system and a corresponding class of Henkin models with ‘logical factoring’. We prove soundness of the calculus in these models, and prove completeness for a restricted fragment.

5.1 Introduction

Much of the intuition for specification and refinement has been presented in the previous two chapters. Let us recall the scenario in which we are studying refinement. We have a programming language and a program logic. In Chapter 3, we showed how to internalise a simple notion of partial development in a programming language, the terms of which, refinement terms, are a record of the stage of development towards a program. In Chapter 4, we studied how to construct a specification language from a program logic, the specifications being given as refinement types. These are orthogonal extensions to the programming language, which in our case is the simply-typed lambda calculus. We now combine these calculi to give a refinement calculus for the stepwise refinement of logical specifications into programs. This claim of orthogonality will be backed up in Section 5.5 below. We now discuss how the features of the subcalculi are combined. The main issue is combining the logic with refinement.

The central language construct is the logical stub. We write ?_{ϕ}, where ϕ is a refinement type, to denote some unknown program with refinement type ϕ, and combine such unknowns with the other language features as in Chapter 3. As in the simpler system, λ_{?}, we will refer to such terms as refinement terms. If ϕ expresses the properties of interest then a refinement will begin with ?_{ϕ}. In general, though, it can be useful to specify with a mixture of logic and program code. Of course, we are now at liberty to ‘overspecify’, and can write specifications which cannot be implemented, even when all types are inhabited. This is in contrast to λ_{?}, and has a bearing on the refinement rules of λ_{⊑}.

One of the slogans of the refinement methodology is that “refinement is correctness-preserving”. To make this clear we must have a notion of partially developed program, that is, refinement term, satisfying a property. In fact, one of the main reasons for keeping an explicit record of the stage of development is that we can draw inferences about partially developed programs. During the course of development, questions might arise of the form, “given that certain implementation steps have now been made, is the final program guaranteed to have a certain property?”

In Chapter 4, we formulated the satisfaction of specifications as programs having a refinement type. We generalise these rules from programs to partially developed programs, that is, we generalise the rules for proving r : τ and t : ϕ to proving r : ϕ. The main technical problem here is combining underdetermined terms with the logic. Since we cannot substitute arbitrary terms for variables we cannot infer that r : (x : ϕ)P from r : ϕ and P [r/x]. Intuitively, we must show that r : ϕ and that every determined term t to which r refines, P [t/x] holds. Let us write this second fact as “∀x ∈ r.P ”. It is cumbersome to prove quantifications like this, however, so observe that we can preserve the truth of such quantifications with rules like

\begin{matrix}∀x ∈ r.P & ∀y ∈ r′.Q \\ ∀z ∈ ⟨r, r′⟩.P [π_{1}z/x] ∧ Q[π_{2}z/y] \end{matrix}

In other words, we can use the refinement type methodology for proving that an arbitrary r has refinement type ϕ, without ever substituting r directly in a proposition.

In Chapter 3, refinement was the decomposition of stubs and their replacement with code. In Chapter 4, the idea was that the refinement of refinement types formalised logical manipulations of specifications. In the combined refinement calculus we combine these two distinct aspects of refinement, by adding a rule that if ϕ refines to ϕ′ then ?_{ϕ} refines to ?_{ϕ′}. Moreover, just as equality in λ_{(:)} is defined with respect to a refinement type, we now extend this idea to refinement at a refinement type, and write the refinement of r to r′ at ϕ as r ⊑_{ϕ} r′.

Hence, there are two complementary aspects to refinement in λ_{⊑}, corresponding to the forms of refinement in the two subcalculi. We can manipulate specifications and replace them with something more specific. This corresponds to the refinement of refinement types in λ_{(:)}, ϕ ⊑_{τ} ϕ′. We can also decompose specifications and replace ‘holes’ in refinement terms with code, and this is formalised by the refinement of refinement terms, r ⊑_{ϕ} r′. One difference from the λ_{?}-calculus is that in λ_{⊑} refinement is under a context with logical assumptions, as given in λ_{(:)}.

Sometimes the particular ϕ at which the refinement is carried out is not important, so we define a notion of ‘nonlogical refinement’ (on refinement terms), r ⊑ r′, meaning: for all ϕ, if r : ϕ then r ⊑_{ϕ} r′. We will use this notion in the formal system. One of the main results of this chapter will be the factorisation of ⊑_{ϕ} into ⊑ and =_{ϕ}.

In Chapter 3, we saw that refinement was a sequence of implementation steps terminating in a program, i.e. a term of λ^{×→}. In the refinement calculus with refinement types, it is more natural to refine to a term of λ_{(:)}. In particular, we take λx : ϕ.t to be determined. This is a natural choice when we only consider terms modulo some refinement type.

Although our notion of program now is not some unique term of λ^{×→}, it is unique up to the equality of some refinement type. It is in the spirit of refinement to only refine as far as is necessary. We can always give a term of λ^{×→} by replacing the refinement types in a λ_{(:)}-term by their underlying types.

5.2 The Calculus

Following the pattern of previous chapters, we give the syntax of the calculus, and the judgement classes. Next we define the notion of λ_{⊑}-axiom system, and give the rules of the calculus.

5.2.1 Syntax

We define an applied refinement calculus by first giving a signature of ground types, constants and predicate symbols. The terms are generated from the same basic signature as in the λ_{(:)}-calculus.

Definition 5.2.1 A λ_{⊑}-signature Sg = ⟨G, K, F⟩ consists of:

• a collection, G, of ground types (ranged over by γ)

• a collection, K, of constants (ranged over by k), each of which has an arity n and sort τ_{1}, …, τ_{n} → τ, which we write as k : τ_{1}, …, τ_{n} → τ.

• a collection, F, of predicate symbols (ranged over by F) each of which has an arity n and sort τ_{1}, …, τ_{n}, which we write as F : Pred (τ_{1}, …, τ_{n}).

Definition 5.2.2 Let Sg = ⟨G, K, F⟩ be a λ_{⊑}-signature. The pre-expressions over Sg are generated by the grammar:

ϕ ::= 1 | γ | Σ_{x:ϕ}ψ | Π_{x:ϕ}ψ | (x : ϕ)P
r ::= x | k(r_{1}, …, r_{n}) | ∗ | ⟨r, r′⟩ | λx : ϕ.r | ?_{ϕ} | π_{1}(r) | π_{2}(r) |
rr′ | let x : ϕ be r in r′
P ::= ⊥ | P ⊃ P′ | ∀x : ϕ.P | F (r_{1}, …, r_{n}) | r ⊑_{ϕ} r′

The pre-contexts are:

Γ ::= ⟨⟩ | Γ, x : ϕ | Γ, P

As in Chapter 4, we write ϕ × ψ and ϕ → ψ for Σ_{x:ϕ}ψ and Π_{x:ϕ}ψ, respectively, when x ∉ F V (ψ). We also abbreviate the assumption x : (x : ϕ)P as x : ϕ|P.

Refinement types have the same meaning as in λ_{(:)}, and correspond to a partial equality over an underlying type.

The specification construct is the logical stub, ?_{ϕ}, for each refinement type, ϕ, meaning ‘some unknown of refinement type ϕ’. The stub also carries the data of when concrete implementations are to be regarded as equal (that is, up to ϕ). This will be made clearer in Section 5.6. In general, λ_{⊑}-terms can be thought of as specifying a collection of programs, up to some equivalence.

We say that a term is determined if it contains no stubs, and otherwise is underdetermined. We use the metavariable t to range over determined terms, and r over arbitrary refinement terms.

Refinement types also appear in the two binding constructs — abstractions and let-terms. This is useful for specification and refinement. When refining the body of the abstraction, λx : ϕ.r, the information in ϕ can be used. The term itself can be thought of as a specification of programs which only constrains the result for arguments in ϕ. We regard λx : ϕ.t as being determined even though, in general, it does not uniquely specify a program in λ^{×→}. These programs are unique up to the equality of some refinement type, however, and we can always give a canonical example by replacing refinement types with the underlying types.

The let-term let x : ϕ be r in r′ is a description of some y in r′[x] for some x in r, where y is only specified up to ϕ. For example, the term

                 let f : even → natbe λn : nat.n in f

specifies the even → nat class which contains λn : nat.n.

The other terms have much the same meaning as in Chapter 3. As in Chapter 4, the interaction between well-formedness and logical reasoning means that we cannot define well-formedness until we give the rules of the calculus, and that axioms are not assumed to be well-formed until their use in a proof.

5.2.2 Judgements

The refinement calculus, λ_{⊑}, consists of two basic judgements.

Γ ⊢ r : ϕ
Γ ⊢ P

where the propositions include refinement of terms, Γ ⊢ r ⊑_{ϕ} r′, and of refinement types, Γ ⊢ ϕ ⊑_{τ} ϕ′. There are also well-formedness judgements

                              ⊢ Γ wf

Γ ⊢ ϕ : Ref (τ)
Γ ⊢ P wf

The judgements extend those of λ_{(:)} in Chapter 4, and have similar intuitive readings. As there, we will use g as a metavariable for syntactic environments, but we use tuples of determined terms, as in λ_{?}.

5.2.3 λ_{⊑}-Axiom Systems

We adopt the same definition of axiom system as in the λ_{(:)}-calculus.

Definition 5.2.3 A λ_{⊑}-axiom system consists of a λ_{⊑}-signature Sg and a collection of λ_{(:)}-axioms Ax formed from pre-contexts and pre-expressions in Sg. Axioms are of two forms:

• propositions in context, Γ ⊢ P

• axioms for constants, Γ ⊢ k : ϕ_{1}, …, ϕ_{n} → ψ.

The comments following Definition 4.3.5 for λ_{(:)} are relevant here too. The restriction of axioms to the λ_{(:)}-fragment is a natural restriction to disallow refinements as axioms. Moreover, this ensures certain metatheoretic properties.

5.2.4 Rules of the Calculus

In Figure 5.1 we summarise the different forms of judgement in the λ_{⊑}-calculus. We can make a basic division into judgements of well-formedness for each syntactic category, and judgements of truth. The division into well-formedness and truth is somewhat arbitrary as all judgements involve logical reasoning, and the refinement typings formalise both well-formedness of terms and the satisfaction of specifications. The upward arrow in Figure 5.1 indicates inclusion of rules. We do not make refinement a separate judgement class from the other propositions. Similarly, the equality rules are just mutual refinements.

Definition 5.2.4 Let ⟨Sg, Ax⟩ be a λ_{⊑}-axiom system. We define the theorems of ⟨Sg, Ax⟩ to be the judgements which can be inferred from the rules in Figures 4.2, 4.3, and 5.2 to 5.12. We write ⟨Sg, Ax⟩▷J when the judgement J is provable from the λ_{⊑}-axiom system ⟨Sg, Ax⟩. We drop the Sg and Ax when they are obvious and just write J, meaning ‘J is provable’.

As for the λ_{(:)}-calculus, we consider the provable well-formedness judgements to be theorems too.

The well-formedness rules for contexts and refinement types are given in Figures 4.2 and 4.3 in Chapter 4. The well-formedness rules for propositions are the natural extensions of those in Chapter 4, with the additional rule that the well-formedness of the refinement r ⊑_{ϕ} r′ requires that r, r′ and ϕ have the same underlying type; they are given in Figure 5.3.

The refinement typing rules in Figures 5.4 and 5.5 are the obvious generalisations of those in Chapter 4, with side-conditions on the elimination rules to ensure that we do not substitute underdetermined terms in refinement types (see Remark 5.2.5 below). For example, the elimination rule for Function Terms has the hypothesis Γ ⊢ r : ϕ → ψ which abbreviates Γ ⊢ r : Π_{x:ϕ}ψ with the side condition that x ∉ F V (ψ).

There are also rules for logical stubs and let-terms.

A special case of the introduction rule for Product Terms is:

\begin{matrix}Γ ⊢ r : ϕ & Γ ⊢ r′ : ψ \\ Γ ⊢ ⟨r, r′⟩ : ϕ × ψ \end{matrix}

The connection between the logic and refinement typing lies in the two rules for Refinement Type Introduction in Figure 5.5. The first rule is actually derivable (as in Chapter 4) but is natural to include. It does not generalise to a rule for arbitrary underdetermined terms, however, since in general, r having

⊢ Γ wf  Figure 4.2
Γ ⊢ P wf  5.3
Well-formedness { Γ ⊢ ϕ : Ref (τ)  4.3
Γ ⊢ r : ϕ  5.4, 5.5
Γ ⊢ P  5.12  } Truth
↑
Γ ⊢ ϕ ⊑_{τ} ϕ′  5.11
Refinement { Γ ⊢ r ⊑_{ϕ} r′  5.9, 5.10
↑
Γ ⊢ t =_{ϕ} t′  5.6
Γ ⊢ r =_{ϕ} r′  5.7, 5.8 } Equality

Figure 5.1: Summary of Judgements in the Refinement Calculus

refinement type (x : ϕ)P can not be encoded as the proposition P [r/x], as discussed in Section 5.1. This is the case, though, for determined terms and for predications.

The Refinement Elimination rule is the generalisation of the Equality rule on p. 105. This is so that refinements can be used to infer refinement typings.

The related rule

{Γ ⊢ r ⊑_{ϕ} r′ \atop Γ ⊢ r′ : ϕ}

is admissible (being a special case of Subject Refinement, Lemma 5.5.2). It would be unnatural to take this rule as primitive and use it in proving refinement typings because this would require guessing the term r.

It is to make Subject Refinement admissible that the λ_{⊑}-calculus has stronger rules for abstractions and let-terms than might be expected:

{Γ, x : ϕ, P ⊢ r : ψ \atop Γ ⊢ λx : ϕ.r : Π_{x:ϕ|P} ψ}

Γ ⊢ r : (x : ϕ)P Γ, x : ϕ, P ⊢ r′ : χ

Γ ⊢ let x : ϕ be r in r′ : χ

(x ∉ F V (χ))

We can derive the obvious simpler forms by letting P be ⊤. We use (x : ϕ)P rather than the general ϕ ⊑ ϕ′ for the same reason as in λ_{(:)} (see p. 105). The corresponding rule for abstractions in λ_{(:)} (inferring that λx : ϕ.t : Π_{x:ϕ|P′}ψ) follows from the rule for equality elimination in λ_{(:)}.

The final rule for refinement typing is a Weakening rule. We need to add this because the axioms are not necessarily closed under weakening.

Figures 5.6 to 5.10 formalise refinement of terms. This includes the equality rules for determined terms in Figure 5.6, and the equality rules for let-terms in Figures 5.7 and 5.8, which are a straightforward extension of those in Chapter 3, replacing arbitrary types with arbitrary refinement types.

The rule for Abstractions is most conveniently given using nonlogical equality, that is, using mutual ⊑ (see p. 137). As mentioned on p. 137, r ⊑ r′ is not a new judgement, as such, but rather a meta-judgement with the meaning: if r has refinement type χ, then r refines to r′ at χ and r′ has refinement type χ. We use different symbols to distinguish the abbreviation, ˆ⊑, from the meta-judgement, ⊑, for clarity’s sake.

Formally, we can write r ˆ⊑r′ in the conclusions of rules, where

{Γ ⊢ J \atop Γ ⊢ r ˆ⊑r′}

abbreviates the schemas (for well-formed ϕ):

\begin{matrix}Γ ⊢ J & Γ ⊢ r : ϕ \\ Γ ⊢ r ⊑_{ϕ} r′ \end{matrix}

\begin{matrix}Γ ⊢ J & Γ ⊢ r : ϕ \\ Γ ⊢ r′ : ϕ \end{matrix}

Superficially, the Abstractions rule is stronger than the form with refinement types:

Γ, x : ϕ, y : ψ ⊢ r[x, y] : ψ′
Γ ⊢ let z : ϕ → ψ be ?_{ϕ→ψ} in λx : ϕ.r[x, zx]
=_{ϕ→ψ′} λx : ϕ.(let y : ψ be ?_{ψ} in r[x, y])

(x ∉ F V (ψ))

though they may be equivalent. In the absence of a proof of equivalence we adopt the former for technical reasons. (We will need this for the Generation Lemma below.)

There is no primitive rule for stubs (though see Chapter 6). We can derive:

let x : ϕ in ?_{(y:τ) Q[x,y]} = ?_{(y:τ) ∃x:ϕ.Q[x,y]}

The other refinement rules for terms are listed in Figures 5.9 and 5.10. It is the rule for Stubs that allows the refinement of refinement types inside terms, and formalises the interaction between the two forms of refinement:

{Γ ⊢ ϕ ⊑ ϕ′ \atop Γ ⊢ ?_{ϕ} ⊑_{ϕ} ?_{ϕ′}}

There is a weakening rule, Refinement Weakening, and a strengthening rule, Logical Congruence.

Finally, Figure 5.11 lists the rules for refinement of refinement types, and Figure 5.12 extends the first-order logic of Chapter 4 with one additional rule of Predicates:

\begin{matrix}Γ, x : ϕ ⊢ F (x) & Γ ⊢ r : ϕ \\ Γ ⊢ F (r) \end{matrix}

This lets us substitute an arbitrary r directly into a predication so we can derive

{Γ ⊢ r : (x : ϕ)F (x) \atop Γ ⊢ F (r)}

We have a Refinement Type Introduction rule in λ_{⊑} for the converse of this:

\begin{matrix}Γ ⊢ r : ϕ & Γ ⊢ F (r) \\ Γ ⊢ r : (x : ϕ)F (x) \end{matrix}

In addition, we must add a rule for a limited form of subject refinement:

\begin{matrix}Γ ⊢ r : (x : ϕ)F (x) & Γ ⊢ r ⊑_{ϕ} r′ \\ Γ ⊢ r′ : (x : ϕ)F (x) \end{matrix}

We would have expected this rule to at least be admissible, but if F (r) is assumed in the context there seems no other way to conclude F (r′). However, it seems that if we can prove F (r) directly, then we can prove F (r′) without using this rule, so the rule is only necessary in this one case.

Hence λ_{⊑} has two refinement type introduction rules and two elimination rules for arbitrary underdetermined terms. The introduction and elimination rules for determined terms can be derived.

Remark 5.2.5 In Chapter 3 we introduced let-terms since we cannot substitute arbitrary terms for variables in terms. Similarly, we cannot substitute arbitrary terms for variables in refinement types. If r : Π_{x:ϕ}ψ and r′ : ϕ then it is not the case that rr′ : ψ[r/x]. However, instead of carrying out a similar extension for refinement types here, we make a restriction on the elimination rules for function and product terms so that this problem does not arise. This is discussed further in Chapter 6.

Axioms  {Γ ⊢ P wf \atop Γ ⊢ P} (Γ ⊢ P ∈ Ax)

Weakening  \begin{matrix}Γ_{1}, Γ_{2} ⊢ B & Γ_{1} ⊢ ϕ wf \\ Γ_{1}, x : ϕ, Γ_{2} ⊢ B \end{matrix}

Γ_{1}, x_{1} : ϕ_{1}, Γ_{2}, x_{2} : ϕ_{2}, Γ_{3} ⊢ B Γ_{1} ⊢ ϕ_{2} wf Γ_{1}, x_{2} : ϕ_{2}, Γ_{2}, x_{1} : ϕ_{1}, Γ_{3} ⊢ B (x^{1} ∉ Γ^{2}, x^{2} : ϕ^{2}) Permutation

Substitution  \begin{matrix}Γ, x : ϕ ⊢ B & Γ ⊢ t : ϕ \\ Γ ⊢ B[t/x] \end{matrix}

Figure 5.2: Theorems Generated from a λ_{⊑}-Axiom System ⟨Sg, Ax⟩

Example 5.2.6 The λ_{(:)}-axiom systems for booleans and naturals from Section 4.3.5 serve also as λ_{⊑}-axiom systems so we do not repeat them here. The main point to be made here is (as for λ_{?}) that we do not need special refinement rules for particular constants. This is important because it means that if we extend the theory with new constants, we need only add equations for determined terms; refinement rules will be automatic from the general rules already in the calculus.

For constant k : ϕ_{1}, …, ϕ_{n} → ψ, there is one refinement rule (omitting the well-formedness hypotheses):

Γ ⊢ ?_{ψ} ⊑_{ψ} k(?_{ϕ_{1}}, …,?_{ϕ_{n}})

For example, since

if then else : P + P′, (x : ϕ) P ⊃ Q[x], (y : ϕ) P′ ⊃ Q[y] → (z : ϕ) Q[z]

we have

?_{(z:ϕ)Q} ⊑ if ?_{P +P′} then ?_{(x:ϕ) P ⊃Q[x]} else ?_{(y:ϕ) P′⊃Q[y]}

Now, if Γ, P ⊢ ?_{(x:ϕ) Q[x]} ⊑ r, then Γ ⊢ ?_{(x:ϕ) P ⊃Q[x]} ⊑ r, so we have an admissible rule for refining to conditionals:

Γ, P ⊢ ?_{(x:ϕ) Q[x]} ⊑ r Γ, P ⊢ ?_{(y:ϕ) Q[y]} ⊑ r′
Γ ⊢ ?_{(z:ϕ)Q} ⊑ if ?_{P +P′} then r else r′

Falsehood  {⊢ Γ wf \atop Γ ⊢ ⊥ wf}
Γ ⊢ P wf Γ ⊢ P′ wf
Γ ⊢ P ⊃ P′ wf

Implication

{Γ, x : ϕ ⊢ P wf \atop Γ ⊢ ∀x : ϕ.P wf} Universal Quantification
Γ ⊢ r_{1} : ϕ_{1} ⋯ Γ ⊢ r_{n} : ϕ_{n} Γ ⊢ ϕ_{1} : Ref (τ_{1}) ⋯ Γ ⊢ ϕ_{n} : Ref (τ_{n}) Γ ⊢ F (r_{1}, …, r_{n}) wf  (F : Pred (τ_{1}, …, τ_{n}) ∈ F) Predication
Γ ⊢ r : ψ Γ ⊢ r′ : ψ′ Γ ⊢ ϕ, ψ, ψ′ : Ref (τ)
Γ ⊢ r ⊑_{ϕ} r′ wf
Γ ⊢ ϕ : Ref (τ) Γ ⊢ ϕ′ : Ref (τ)
Γ ⊢ ϕ ⊑_{τ} ϕ′ wf

Refinement Figure 5.3: Well-formedness of Propositions

Variables  {⊢ Γ, x : ϕ, Γ′ wf \atop Γ, x : ϕ, Γ′ ⊢ x : ϕ}

Γ ⊢ ϕ_{1} : Ref (τ_{1}) ⋯ Γ ⊢ ϕ_{n} : Ref (τ_{n}) Γ ⊢ ψ : Ref (τ) Γ ⊢ r_{1} : ϕ_{1} ⋯ Γ ⊢ r_{n} : ϕ_{n} Γ ⊢ k(r_{1}, …, r_{n}) : ψ  {Γ′ ⊢ k : ϕ_{1}, …, ϕ_{n} → ψ ∈ Ax; ; k : τ_{1}, …, τ_{n} → τ ∈ K; ; Γ′ ⊆ Γ Constants

Unit  {⊢ Γ wf \atop Γ ⊢ ∗ : 1}

Stubs  {Γ ⊢ ϕ wf \atop Γ ⊢?_{ϕ} : ϕ}

Γ ⊢ r : ϕ Γ, x : ϕ ⊢ r′ : ψ

Γ ⊢ let x : ϕ be r in ⟨x, r′⟩ : Σ_{x:ϕ}ψ

{Γ ⊢ r : ϕ × ψ \atop Γ ⊢ π_{1}(r) : ϕ}  {Γ ⊢ r : ϕ × ψ \atop Γ ⊢ π_{2}(r) : ψ}

Product Terms

Function Terms  {Γ, x : ϕ, P ⊢ r : ψ \atop Γ ⊢ λx : ϕ.r : Π_{x:ϕ|P} ψ} ; \begin{matrix}Γ ⊢ r : ϕ → ψ & Γ ⊢ r′ : ϕ \\ Γ ⊢ rr′ : ψ \end{matrix}

Γ ⊢ r : (x : ϕ)P Γ, x : ϕ, P ⊢ r′ : ψ Γ ⊢ let x : ϕ be r in r′ : ψ (x ∉ F V (ψ)) Let Terms

Figure 5.4: Refinement Typings

\begin{matrix}Γ ⊢ t : ϕ & Γ ⊢ P [t/x] \\ Γ ⊢ t : (x : ϕ)P \end{matrix}

\begin{matrix}Γ ⊢ r : ϕ & Γ ⊢ F (r) \\ Γ ⊢ r : (x : ϕ)F (x) \end{matrix}

\begin{matrix}Γ ⊢ r : (x : ϕ)F (x) & Γ ⊢ r ⊑_{ϕ} r′ \\ Γ ⊢ r′ : (x : ϕ)F (x) \end{matrix}

Refinement Type Introduction

Refinement Elimination {Γ ⊢ r ⊑_{ϕ} r′ \atop Γ ⊢ r : ϕ}

Weakening  \begin{matrix}Γ ⊢ r : ϕ′ & Γ ⊢ ϕ ⊑ ϕ′ \\ Γ ⊢ r : ϕ \end{matrix}

Figure 5.5: Refinement Typings cont.

The derived refinement rule for natwfrec (the constant for well-founded recursion) is particularly interesting, as rules for ‘recursive refinement’ are central to many refinement calculi (see, for example, [Bun97], p. 46). The axiom is

natwfrec : (Π_{x:nat}(Π_{z<x}ϕ[z]) → ϕ[x]) → Π_{x:nat}ϕ[x]

so, after simplifying the well-formedness hypotheses, the refinement rule is

Γ, x : nat ⊢ ϕ[x] wf
Γ ⊢ ?_{Π_{x:nat}ϕ[x]} ⊑ natwfrec (?_{Π_{x:nat}(Π_{z<x}ϕ[z])→ϕ[x]})

We can then derive the rule of recursive (or ‘circular’) refinement:

Γ, n : nat ⊢ ?_{ϕ[n]} ⊑_{ϕ[n]} t[n, λm : nat|m < n.?_{ϕ[m]}]
Γ ⊢ ?_{Π_{n:nat}ϕ[n]} ⊑_{Π_{n:nat}ϕ[n]} natwfrec (λn.λf.t[n, f])

Compare this with the discussion of equality on p. 113. This rule is equivalent to (Intro. rec. func.) on p. 46 of [Bun97].

Remark 5.2.7 As mentioned in Remark 3.2.13, a general form of β-equality does not hold in the λ_{⊑}-calculus; instead we have β-equality only for determined

Γ ⊢ ϕ_{1} : Ref (τ_{1}) ⋯ Γ ⊢ ϕ_{n} : Ref (τ_{n}) Γ ⊢ ψ : Ref (τ) Γ ⊢ t_{1} =_{ϕ_{1}} t′_{1} ⋯ Γ ⊢ t_{n} =_{ϕ_{n}} t′_{n} Γ ⊢ k(t_{1}, …, t_{n}) =_{ψ} k(t′_{1}, …, t′_{n})  {Γ′ ⊢ k : ϕ_{1}, …, ϕ_{n} → ψ ∈ Ax ; ; k : τ_{1}, …, τ_{n} → τ ∈ K; ; Γ′ ⊆ Γ Constant Equations
\begin{matrix}Γ, x : ϕ ⊢ t : ψ & Γ ⊢ t′ : ϕ \\ Γ ⊢ (λx : ϕ.t)t′ =_{ψ[t′/x]} t[t′/x] \end{matrix}  (β)
{Γ, x : ϕ ⊢ tx : ψ \atop Γ ⊢ λx : ϕ.tx =_{Π_{x:ϕ}ψ} t} (x ∉ F V (t))  (η)
\begin{matrix}Γ, x : ϕ ⊢ P wf & Γ, x : ϕ, P ⊢ t =_{ψ} t′ \\ Γ ⊢ λx : (x : ϕ)P.t =_{Π_{x:ϕ|P} ψ} λx : ϕ.t′ \end{matrix}  (ξ)

Function Equations

\begin{matrix}Γ ⊢ t_{1} : ϕ_{1} & Γ ⊢ t_{2} : ϕ_{2} \\ Γ ⊢ π_{1}⟨t_{1}, t_{2}⟩ =_{ϕ_{1}} t_{1} \end{matrix}  \begin{matrix}Γ ⊢ t_{1} : ϕ_{1} & Γ ⊢ t_{2} : ϕ_{2} \\ Γ ⊢ π_{2}⟨t_{1}, t_{2}⟩ =_{ϕ_{2}} t_{2} \end{matrix}  (β)
\begin{matrix}Γ ⊢ π_{1}(t) : ϕ & Γ ⊢ π_{2}(t) : ψ[π_{1}(t)/x] \\ Γ ⊢ ⟨π_{1}(t), π_{2}(t)⟩ =_{Σ_{x:ϕ}ψ} t \end{matrix}  (η)

Product Equations

Unit Equation  {Γ ⊢ t : 1 \atop Γ ⊢ t =_{1} ∗}

Figure 5.6: Equality Rules for Determined Terms

Γ ⊢ t : ϕ Γ, x : ϕ ⊢ r : ψ
Γ ⊢ let x : ϕ be t in r =_{ψ[t/x]} r[t/x]

Let Beta

Γ ⊢ r : ϕ
Γ ⊢ let x : ϕ be r in x =_{ϕ} r

Let Eta

Γ ⊢ r : ϕ Γ, y : ϕ ⊢ r′ : ψ Γ, x : ψ ⊢ r′′ : ψ′
Γ ⊢ let x : ψ be (let y : ϕ be r in r′[y]) in r′′[x]
=_{ψ′} let y : ϕ be r in (let x : ψ be r′[y] in r′′[x])

Let Associativity

Figure 5.7: Let Term Equalities

arguments and bodies. This is unlike refinement calculi based on nondeterminism (such as [Bun97] and [Mor94]). To illustrate this, let n : nat ⊢ Fermat(n) : Ref (nat × nat × nat) be the specification of solutions to Fermat’s Last Theorem at index n (i.e. tuples ⟨x, y, z⟩ such that x^{n} + y^{n} = z^{n}). Then, in contrast to λ_{⊑},

[Bun97] and [Mor94] both have:

                   (λn : nat.?_{Fermat(n)})2 = Fermat(2)

In λ_{⊑}, the left hand side is unsatisfiable, whereas the right hand side is satisfiable.

This is similar to the situation with ASL and Extended ML. As observed in [SST92] (Section 4.3), the “principle of modular decomposition” means that if a module is decomposed into the application of a parameterised module to some other module, the parameterised module must be implemented for arbitrary arguments so as to be implementation independent, and not make use of particular properties of the actual argument. A similar point was made in Example 3.2.4. We discuss EML in more detail in Section 5.4.1 below.

Although the general β-equality does not hold in λ_{⊑}, we do have the inequality, r[t/x] ⊑ (λx : ϕ.r)t, for underdetermined r. This means that we can use the common programming technique of refining by first abstracting from a specific t, and then implementing recursively for a general argument. For example, ?_{ϕ[t]} ⊑ (λx.?_{ϕ[x]})t, and we could then use recursive refinement to get natwfrec (λn.λf.u[n, f]) t for some u.

Γ ⊢ r_{1} : ϕ_{1} ⋯ Γ ⊢ r_{n} : ϕ_{n} Γ ⊢ let x_{1} : ϕ_{1}, …, x_{n} : ϕ_{n} be r_{1}, …, r_{n} in k(x_{1}, …, x_{n}) =_{ψ} k(r_{1}, …, r_{n}) (Γ ⊢ k : ϕ_{1}, …, ϕ_{n} → ψ ∈ Ax) Constants
Γ ⊢ r : ϕ → ψ Γ ⊢ r′ : ϕ

Γ ⊢ let x : ϕ → ψ, x′ : ϕ be r, r′ in xx′ =_{ψ} rr′ Applications

Γ ⊢ r : ϕ Γ ⊢ r′ : ψ

Γ ⊢ let x : ϕ, x′ : ψ be r, r′ in ⟨x, x′⟩ =_{ϕ×ψ} ⟨r, r′⟩ Pairs

Γ ⊢ r : ϕ_{1} × ϕ_{2}

Γ ⊢ let x : ϕ_{1} × ϕ_{2} be r in π_{i}(x) =_{ϕ_{i}} π_{i}(r)

Projections

Γ, x : ϕ, y : ψ ⊢ r[x, y] : ψ′

Γ ⊢ let z : Π_{x:ϕ}ψ be ?_{Π_{x:ϕ}ψ} in λx : ϕ.r[x, zx] ˆ= λx : ϕ.(let y : ψ be ?_{ψ} in r[x, y])

Abstractions Figure 5.8: Let Term Equalities cont.

Variables  {⊢ Γ, x : ϕ, Γ′ wf \atop Γ, x : ϕ, Γ′ ⊢ ?_{ϕ} ⊑_{ϕ} x}
Γ ⊢ ϕ_{1} : Ref (τ_{1}) ⋯ Γ ⊢ ϕ_{n} : Ref (τ_{n}) Γ ⊢ ψ : Ref (τ) Γ ⊢ ?_{ψ} ⊑_{ψ} k(?_{ϕ_{1}}, …,?_{ϕ_{n}})  {Γ′ ⊢ k : ϕ_{1}, …, ϕ_{n} → ψ ∈ Ax ; ; k : τ_{1}, …, τ_{n} → τ ∈ K; ; Γ′ ⊆ Γ Constants
Unit  {⊢ Γ wf \atop Γ ⊢ ?_{1} ⊑_{1} ∗}
Pairs  {Γ ⊢ ϕ × ψ wf \atop Γ ⊢ ?_{ϕ×ψ} ⊑_{ϕ×ψ} ⟨?_{ϕ},?_{ψ}⟩}
Abstractions  {Γ ⊢ Π_{x:ϕ}ψ wf \atop Γ ⊢ ?_{Π_{x:ϕ}ψ} ⊑_{Π_{x:ϕ}ψ} λx : ϕ.?_{ψ}}

Figure 5.9: Refinement Rules

Stubs  {Γ ⊢ ϕ ⊑ ϕ′ \atop Γ ⊢ ?_{ϕ} ⊑_{ϕ} ?_{ϕ′}}

\begin{matrix}Γ, x : ϕ ⊢ P′ ⊃ P & Γ, x : ϕ, P′ ⊢ r ⊑_{ψ} r′ \\ Γ ⊢ λx : (x : ϕ)P .r ⊑_{Π_{x:ϕ|P′}ψ} λx : ϕ.r′ \end{matrix}

Γ ⊢ ϕ ⊑ ϕ′ Γ ⊢ r_{1} ⊑_{ϕ′} r′_{1} Γ, x : ϕ ⊢ r_{2} ⊑_{ψ} r′_{2}

Γ ⊢ let x : ϕ be r_{1} in r_{2} ⊑_{ψ} let x : ϕ′ be r′_{1} in r′_{2} (x ∉ F V (ψ))

Γ, x : ϕ ⊢ P wf Γ ⊢ r_{1} =_{(x:ϕ)P} r′_{1} Γ, x : ϕ, P ⊢ r_{2} =_{ψ} r′_{2}

Γ ⊢ let x : (x : ϕ)P be r_{1} in r_{2} =_{ψ} let x : ϕ be r′_{1} in r′_{2}

(x ∉ F V (ψ))

Congruence

\begin{matrix}Γ ⊢ r ⊑_{ϕ} r′ & Γ ⊢ r : (x : ϕ)P \\ Γ ⊢ r ⊑_{(x:ϕ)P} r′ \end{matrix} Logical Congruence
Reflexivity  {Γ ⊢ r : ϕ \atop Γ ⊢ r ⊑_{ϕ} r}
Transitivity  \begin{matrix}Γ ⊢ r ⊑_{ϕ} r′ & Γ ⊢ r′ ⊑_{ϕ} r′′ \\ Γ ⊢ r ⊑_{ϕ} r′′ \end{matrix}
Γ ⊢ r′ : ψ Γ ⊢ r : ϕ Γ ⊢ r′ ⊑_{ψ} let x : ϕ be r in r′ (x ∉ F V (r′)) Let Weakening
\begin{matrix}Γ ⊢ r ⊑_{ϕ′} r′ & Γ ⊢ ϕ ⊑ ϕ′ \\ Γ ⊢ r ⊑_{ϕ} r′ \end{matrix} Refinement Weakening

Figure 5.10: Refinement Rules cont.

Unit  {⊢ Γ wf \atop Γ ⊢ 1 ⊑ 1}
Product  \begin{matrix}Γ ⊢ ϕ ⊑ ϕ′ & Γ, x : ϕ ⊢ ψ ⊑ ψ′ \\ Γ ⊢ Σ_{x:ϕ}ψ ⊑ Σ_{x:ϕ′}ψ′ \end{matrix}
Function  \begin{matrix}Γ ⊢ ϕ′ ⊑ ϕ & Γ, x : ϕ ⊢ ψ ⊑ ψ′ \\ Γ ⊢ Π_{x:ϕ}ψ ⊑ Π_{x:ϕ′}ψ′ \end{matrix}
Refinement Types \begin{matrix}Γ ⊢ ϕ ⊑ ψ & Γ, x : ψ ⊢ P \\ Γ ⊢ (x : ϕ)P ⊑ ψ \end{matrix} ; {Γ, x : ψ, Q ⊢ x : ϕ \atop Γ ⊢ ϕ ⊑ (x : ψ)Q}
Transitivity  \begin{matrix}Γ ⊢ ϕ ⊑ ϕ′ & Γ ⊢ ϕ′ ⊑ ϕ′′ \\ Γ ⊢ ϕ ⊑ ϕ′′ \end{matrix}

Figure 5.11: Refinements on Refinement Types

Predicates  \begin{matrix}Γ, x : ϕ ⊢ F (x) & Γ ⊢ r : ϕ \\ Γ ⊢ F (r) \end{matrix}

Together with the rules in Figure 4.10, Chapter 4. Figure 5.12: First-order Logic for the Refinement Calculus

Finally, it is interesting, in retrospect, that we do have the general β-equality for the λ_{?} fragment, as discussed on p. 67. This means that, to a certain extent, we can evaluate such terms as though they were programs.

One other illustration of the difference between λ_{⊑} and two refinement calculi based on nondeterminism, [Bun97] and [War94], is that, as for call-by-value nondeterminism, arbitrary abstractions are considered to be values, i.e. nondeterministic functions.

5.3 An Example of Refinement

The example we will consider involves sorting association lists of keys and complex values. The idea behind association lists is that values of a complex datatype can be manipulated efficiently by pairing them with keys that encode some useful information.

We will develop two programs: first a sorting function and, then, a function which determines whether or not two lists are permutations. We use the axiom system for naturals and booleans given in Example 5.2.6 and extend it with an axiom system for keys, values and association lists (though we will only give some of the axioms).

The axiom system ⟨Sg_{Assoc}, Ax_{Assoc}⟩ is defined as follows. Let Sg_{Assoc} =

⟨G_{Assoc}, K_{Assoc}, F_{Assoc}⟩, where

                   G_{Assoc} = {key, value, assoclist}

   K_{Assoc} = {eq key, compare key, nil, cons, head, tail, remove, listrec}

                   F_{Assoc} = {Ordered, In, Sublist}

The ground types are key, value and assoclist. We define association pairs

as assocpair = key × value.

There are efficient equality and comparison functions on keys:

                    compare key : key, key → bool
                      eq key : key, key → bool

The signature for lists is:

                          nil : assoclist
               cons : assocpair, assoclist → assoclist
                    head : assoclist → assocpair

                    tail : assoclist → assoclist
              remove : assocpair, assoclist → assoclist
      listrec : τ, (assoclist → assocpair → τ → τ), assoclist → τ

The constants head and tail are defined for all lists, including nil. However, the axioms do not say what the values at nil are. We use listrec, which is primitive recursion over lists (fold left in functional programming).

The axioms include:

              head : (l : assoclist) l ≠ nil → assocpair
              tail : (l : assoclist) l ≠ nil → assoclist
                        listrec (t, f, nil) = t

listrec (t, f, cons(x, xs)) = f xs x (listrec (t, f, xs))

l : assoclist ⊢
  listrec : ϕ[nil], Π_{l′:assoclist}Π_{p:assocpair}Π_{x:ϕ[l′]}ϕ[cons(p, l′)], {l}_{assoclist} → ϕ[l]

We will use the predicate symbols:

       Ordered : Pred (assoclist, assocpair → assocpair → bool)
                   In : Pred (assocpair, assoclist)
                Sublist : Pred (assoclist, assoclist)

with the axioms ∀p : assocpair.¬In(p, nil) ∧ ∀p.∀x.∀xs.In(p, x :: xs) ⟺ p = x ∧ In(p, xs)

                         ∀l′ . Sublist(nil, l′)

∀x.∀l.∀l′ . Sublist(cons(x, l), l′) ⟺ In(x, l′) ∧ Sublist(l, remove(x, l′)) Ordered(nil, <) ∧ x′ < x ∧ Ordered(x :: xs, <) ⊃ Ordered(x′ :: x :: xs, <)

where Ordered(l, <) holds when the list l is sorted relative to ordering <, and In(p, l) holds when the pair p is in the list l, that is, mathematically in, rather than in terms of the key.

For l, l′ : assoclist, the proposition Perm[l, l′] is defined as:

Perm[l, l′] ≡ Sublist(l, l′) ∧ Sublist(l′, l)

We specify the ordering on association pairs as

               compare : assocpair → assocpair → bool

∀k, k′ : key.∀v, v′ : value .

       compare key (k, k′) = true ⊃ compare ⟨k, v⟩ ⟨k′, v′⟩ = true

For example,

    compare = λa : assoclist.λa′ : assoclist.compare key(π_{1}(a), π_{1}(a′))

and the sorting function as

              sort spec : Ref (assoclist → assoclist)
   sort spec ≡ Π_{l:assoclist}(l′ : assoclist) Ordered(l′, compare) ∧ Perm[l, l′]

Note that this specification does not say what should happen when two values have the same key, but this does not matter for now. We remark that the semantics of ?_{sort spec} is truly underdetermined.

Define ϕ[l] to be the specification ‘is a sorting of l’:

    (l′ : assoclist) Ordered(l′, compare) ∧ Perm[l, l′] : Ref (assoclist)

The refinement begins as:

?_{sort spec}
  ⊑ λl : assoclist .?_{(l′:assoclist) Ordered(l′,compare) ∧ Perm[l,l′]}
  ⊑ λl : assoclist . listrec (?_{ϕ[nil]},?_{Π_{l′:assoclist}Π_{p:assocpair}Π_{x:ϕ[l′]}ϕ[cons(p,l′)]},?_{{l}_{assoclist}})
  ⊑ λl : assoclist . listrec (nil,?_{Π_{l′:assoclist}Π_{p:assocpair}Π_{x:ϕ[l′]}ϕ[cons(p,l′)]}, l)

We now plan the next stage of the implementation. One possibility is to construct a new list by systematically removing elements, and inserting them in the correct position in a new list. This is an insertion sort. We need to implement the specification

Π_{l′:assoclist}Π_{p:assocpair}Π_{x:ϕ[l′]}ϕ[cons(p, l′)] : Ref(assoclist → assocpair → τ → τ)

In fact, we do not need to use the first argument. The specification of an insertion function is (for l′ : assoclist):

   insert spec ≡ Π_{p:assocpair}Π_{x:ϕ[l′]}ϕ[cons(p, l′)]
                      : Ref (assocpair → assoclist → assoclist)

which says ‘given p : assocpair and x a sorting of l′, return a sorting of cons(p, l′)’, that is, ‘insert p in the correct position in the sorted list x’. Thus,

        ?_{Π_{l′:assoclist}Π_{p:assocpair}Π_{x:ϕ[l′]}ϕ[cons(p,l′)]} ⊑ λl′ : assoclist.?_{insert spec}

We use the refinement rule for listrec again to refine insert spec. For m : assoclist, let ψ[m] ≡ ϕ[cons(p, m)]. We have

?_{insert spec}

⊑ λp : assocpair.λx : ϕ[l′].?_{ψ[l′]}
⊑ λp : assocpair.λx : ϕ[l′].listrec (?_{ψ[nil]},?_{Π_{m:assoclist}Π_{p′:assocpair}Π_{y:ψ[m]}ψ[cons(p′,m)]}, x)
⊑ λp : assocpair.λx : ϕ[l′].listrec (cons(p, nil),?_{Π_{m:assoclist}Π_{p′:assocpair}Π_{y:ψ[m]}ψ[cons(p′,m)]}, x)

Then since we can prove

p : assocpair, x : ϕ[l′], m : assoclist, p′ : assocpair, y : ψ[m] ⊢
    ifcompare p′ p thencons(p′, y) elsecons(p, cons(p′, m)) : ψ[cons(p′, m)]

we have

p : assocpair ⊢ ?_{Π_{m:assoclist}Π_{p′:assocpair}Π_{y:ψ[m]}ψ[cons(p,m)]} ⊑
       λm : assoclist.λp′ : assocpair.λy : ψ[m] .
               ifcompare p′ p thencons(p′, y) elsecons(p, cons(p′, m))

This is the only step of the refinement that generates a proof obligation. We can now give the code for the sorting algorithm:

      sort = λl : assoclist . listrec (nil, λl′ : assoclist.insert, l)

where insert is λp : assocpair.λx : ϕ[l′].

        listrec ([p],

            λm : assoclist.λp′ : assocpair.λy : ψ[m] .

               ifcompare p′ p thencons(p′, y) elsecons(p, cons(p′, m)), x)

We are guaranteed (by subject refinement; see Lemma 5.5.2) that sort :

sort spec.

Now suppose we want to write a function to test whether two association lists are (true) permutations of each other, and so implement Perm. We can sort the lists using sort and compare corresponding entries using compare key. This is not quite right, though, since as pointed out above, distinct values may have the same key and so end up with different relative orderings in separate lists. We would like to specify that our sorting is, in some sense, ‘context independent’, in the sense that two pairs in a list will always be sorted in the same order, no matter where they appear in a list. This can be done by strengthening the specification on insert so that in the case when two values have the same key, the insert function makes some predetermined choice dependent only on the value. We require

∀v, v′ : value . v ≠ v′ ⊃ choose(v, v′) ⟺ ¬choose(v′, v)

We specify

             insert′ : assocpair → assoclist → assoclist
∃choose : value → value → bool . ∀p : assocpair . ∀l : assoclist .
               Ordered(insert′ p l, compare′) ∧ Perm[insert p l, cons(p, l)]

where compare′ is a lexicographic ordering that first orders on the key, then on choose: compare′ ⟨k, v⟩ ⟨k′, v′⟩ =

    if (compare key(k, k′) = true) thentrueelse
         if (eq key(k, k′) = true) then choose v v′ elsefalse

Thus, we leave it up to the implementer of insert′ to find some injective ordering, perhaps by exploiting implementation details of the values.

We can now define the permutation function as

                perm : assoclist → assoclist → bool
                 perm l_{1} l_{2} = eqlist (sort′ l_{1}) (sort′ l_{2})

where eqlist is defined using listrec, and sort′ uses insert′.

Remark 5.3.1

The functions head and tail are partial in the sense that their results are not defined for the argument nil. In [vL90], various ways of accounting for partiality in algebraic specification are considered. There are two ways in which the simple approach of total algebras can be extended. On the one hand, we can extend the syntax of specifications; on the other, the algebra semantics.

With error algebras, the specification is augmented with error values at each type, together with predicates to distinguish between error and non-error values, and axioms to explain how errors are propagated. This complicates specifications considerably, but terms can still be interpreted as total functions.

An extension of this idea is to use monotonic (or continuous) algebras, where each type is axiomatised as a poset (or cpo). Again, terms are interpreted as total functions, but must be monotonic (or continuous) with respect to the orderings.

An alternative approach is to just change the definition of algebras, rather than the specifications. In a partial algebra, terms are interpreted as partial functions over the carrier sets. This necessitates a change in the definition of homomorphism, satisfaction, and so on.

An approach which alters both the notion of specification and algebra is order-sorted algebras. This is a form of subtype polymorphism with an ordering defined on the sorts. The idea is to give terms more specific sorts so they become total. For example, we can define Nonemptylist ≤ List and then have

tail : Nonemptylist → List.

The approach we have used here differs from each of these ideas. Although we have a form of subtyping, this is not used to constrain the domains of primitive functions. Instead, by regarding terms as representatives of equivalence classes we can abstract away irrelevant details. This seems a natural approach because, at the end of the day, we write total programs in the underlying programming language. We might say that such functions are computationally total but specificationally partial.

5.4 Comparisons

We compare λ_{⊑} with some related approaches to program development. Extended ML and Lego are based on the same notion of refinement as λ_{⊑}. Although the calculi of Morgan et.al. are also refinement calculi, they are based on nondeterminism. We show how λ_{⊑} can be used as a metalanguage for studying and comparing program development methodologies. We also compare with two alternative approaches to program development, based on type theory.

5.4.1 Extended ML

The Extended ML language (EML) is similar to spirit to our approach in that it takes an existing language, in this case Standard ML, and conservatively extends it with specification constructs to give a wide-spectrum development language. Moreover, the constructs added – placeholders (“question marks”) and axioms – corresponds exactly to those of our modular analysis here. There is a well-defined semantics [KST97] and methodology [San91].

The semantics is separated into static, dynamic and verification parts. The static semantics is analogous to finding the underlying type of a term, which we have not formalised directly. Refinement typing corresponds to both static and verification semantics. One difference is that a verification checks that an abstract program is well-annotated with respect to a particular interpretation, rather than showing that a particular property holds for all interpretations as we do. The dynamic semantics formalises the evaluation of terms, whenever possible, in order to ‘experiment’ with abstract programs. Although we have not formalised this, we suggest how this could be considered here on p. 67.

The intention in EML is to formalise the specification language in terms of an arbitrary logic (or rather, an institution). As here, the specification style is property-oriented.

Terms are interpreted with respect to (amongst other things) a particular “question mark interpretation”. This is a syntactic mapping of ?’s to arbitrary expressions.

There are a number of other differences. The question marks can replace arbitrary expressions and so, in particular, types. Booleans and propositions are combined. Satisfaction of properties is up to behavioural equivalence.

There is no proof theory. Rather, three general forms of refinement rule are given, any particular application of which generates proof obligations which must be verified with respect to the semantics. In contrast, our rules are low-level and have been proven sound (and complete). The rules of [San91] (in a suitably translated form) are admissible in λ_{⊑}.

To take a simple example, if we model functors

functor F (X0 : SIG0) : SIG0′ = exp[X0]

as abstractions λX0 : SIG0.exp[X0], then the coding rule of [San91] can be derived. The rule becomes

λX : SIG.?_{SIG′} ⊑ λX : SIG.r

when “SIG ∪ r ⊨ SIG′”, that is,

X : SIG ⊢ r : SIG′

This follows since it is admissible that if r : ϕ then ?_{ϕ} ⊑ r.

5.4.2 Aspinall’s λ_{ASL+}

In his thesis [Asp97], Aspinall presents a number of lambda-calculus based calculi for program development. In the same spirit as our work, he constructs his main calculus from a number of subcalculi which he studies separately.

The development methodology is based on the “specification as type, elementhood as satisfaction, subtyping as refinement” idea, but the specification language is parameterised with respect to an arbitrary institution. The underlying type theory is not used as a specification language, however, but gives a type structure to the specification building operations of the institution.

The two subcalculi, λ_{≤{}} and λ_{P ower}, are extensions of the dependently-typed lambda calculus with singleton and power types respectively. The singleton types are a simple form of specification (independent of the institution) while the power types allow parameterisation of specifications over arbitrary specifications. We have not studied parameterised specifications however.

There are significant similarities between λ_{≤{}} and λ_{(:)}. Although Aspinall’s intention was to provide a framework in which modular specification constructors could be studied independently of any particular logic, since specifications of functions are a simple form of ‘specification of parameterised program’, this gives a specification language anyway. The notion of refinement defined in λ_{≤{}} only accounts for singleton types, and not general propositions as in λ_{(:)}. Rather, propositions are added on top with the institution.

Aspinall’s calculi are parameterised by a signature, and a consequence relation over that signature which satisfies certain properties. In contrast, the axiom systems used here are defined as a signature and an explicit set of axioms which are then used with the inference rules.

Although specifications are treated as types, he has a notion of rough type (originally due to Sannella) which is analogous to the underlying types here.

Another similarity is that he also uses a per semantics, interpreting specifications as pers over the underlying type. However, he interprets terms as elements of pers whereas we interpret them as equivalence classes. This is evident in the interpretation of abstractions, λx : ϕ.t. Aspinall does not take account of the ϕ but, rather, uses the (rough) type of ϕ. However, he does not have any completeness results.

5.4.3 Type Theory

There are two general approaches to using type theory for program derivation. On the one hand, there is the subtyping approach, as exemplified by Sannella and Tarlecki [ST87], and Aspinall. There, specifications are formalised as types, and the refinement of specifications is formalised as a subtyping spec ≤ spec′. Refinement continues until it is obvious that some program satisfies the specification, that is, inhabits the type.

The other approach exploits the constructive nature of type theory via the Curry-Howard isomorphism. A specification is phrased as a theorem so that the proof of this theorem in the constructive logic of type theory automatically gives a program which satisfies a specification, via some extraction mechanism.

In [NPS90], Nordström, Petersson and Smith, present Martin-Löf’s type theory as a unified formalism for specification and programming based on the extraction style. The derivation methodology is based on the idea that the typing rules can be read as goal-directed tactics.

There are two levels to Martin-Löf type theory: the basic type theory of dependent types, and on top of this, a theory with subset types. A subset type, {x : τ | P }, consists of a type τ and propositional function, P and types those terms with type τ for which P is true.

This split into two levels is similar to that of types and refinement types here, but there is an important difference in that refinement types correspond to relations over types rather than subsets. We could regard λ_{(:)} as formalising an alternative interpretation of the subset theory.

Some consequences of using this type-theoretic formalism for specification and programming are that the logic is intuitionistic and all programs terminate. With subset types, {x : τ |P }, the proposition P is translated into the underlying type theory, and so must be intuitionistic.

The interpretation of the subset theory in the basic theory is given as a translation of a type into basic types and propositional functions. For example, even → even is translated to the type nat → nat and the propositional function ∀x : nat . Even(x) ⊃ Even(fx) in f. The typing rules can be translated in this way because they use Curry style rules where abstractions are not labelled with types.

Program refinement, as conceived in this thesis, has similarities with both of the type-theoretic approaches. The refinement relation of λ_{?} corresponds to the program extraction approach (where refinement is often implicit), whereas that of λ_{(:)} corresponds to the subtyping approach.

5.4.4 Lego

Lego [LP92] is an example of a proof assistant which implements the extraction style of type-theoretic development. There is no subtyping, but instead an explicit notion of refinement based on existential variables.

At any stage during a refinement in Lego, the user is presented with a proof state consisting of a context of assumptions x_{1} : ϕ_{1}, …, x_{n} : ϕ_{n}, and a number of goals ?1 : ψ_{1}, …?m : ψ_{m}. There is also a stored representation of the proof so far, which is hidden from the user, and any other goals which are out of context. Naively, we might represent this state as the refinement term λx_{1} : ϕ_{1}, …, x_{n} : ϕ_{n} . let y_{1} : ψ_{1}, …, y_{m} : ψ_{m} in t where t is a translation of the proof so far. To see how goals out of context arise, suppose the first goal is ?1 : ψ_{1} → ψ′_{1} and that we refine this. The resulting proof state contains assumptions x_{1} : ϕ_{1}, …, x_{n} : ϕ_{n}, z : ψ_{1} and the single goal ?m + 1 : ψ′_{1}. The other goals are hidden since we cannot use the assumption z : ψ_{1} to refine them. In fact, the proof states correspond to arbitrary refinement terms. To a certain extent, the user is able to manipulate terms which contain existential variables ?n : ψ_{n} corresponding to refinement terms.

This is more sophisticated than the approach of [NPS90] since it incorporates existential variables and an explicit notion of refinement, all of which is implicit in the straightforward type-theoretic approach.

Existential variables correspond to stubs rather than free variables. In fact, our refinement calculus may be viewed as an explicit formalisation of Lego’s refinement process. Conversely, Lego may be viewed as a tool for performing refinement. Although a closer comparison would be between Lego and version of λ_{?} for the calculus of constructions, we consider underdeterminism to be orthogonal to the type theory.

The basic commands in Lego are setting a goal, claiming a lemma, making a local definition, refining the current goal, and changing the order of goals. Each of these commands corresponds naturally to a refinement step in our calculus.

The first step in a development, setting a goal ϕ, introduces the refinement term let x : ϕ in x; claiming a lemma is refinement by let-weakening; while making a local definition x = t is also a let-weakening, though t must be determined (i.e. not contain any existential variables).

Refinement of goals in Lego is performed by directly solving a goal, unifying it with another goal, or using some library function f : ψ_{1}, ψ_{2} → ϕ, so that ?1 : ϕ is refined to ?2 : ψ_{1},?3 : ψ_{2}. We translate this as

f : ψ_{1} × ψ_{2} → ϕ

let x : ϕ in t[x] ⊑ let y_{1} : ψ_{1}, y_{2} : ψ_{2} in t[f(y_{1}, y_{2})]

Rearrangement of goals corresponds to the commutativity of let-terms. We could regard this as a nontrivial justification for the reordering of goals in Lego, though for the simpler expressions arising here though, it is more obviously sound! Lego has some ability to perform automatic unification during refinement. Such steps are derived from more basic ones.

It would be interesting to formally compare the rules of Lego with those of λ_{⊑}. This would let us apply some of the metatheoretic results here to Lego. For example, if Lego has all the rules of λ_{?} we could conclude that claims are unnecessary.

5.4.5 Refinement Calculus of Back, Morgan and Morris

We compare our calculus with the imperative refinement calculus of Back, Morgan and Morris. This version is taken from [Mor94]. A simplified grammar of the language is:

C ::= skip | abort | x := E | if P_{1} → C_{1} [] … [] P_{n} → C_{n} fi | re x.C er |

if P then C else C′ | x⃗ : [P, P′] | var x : σ • C | con x : σ • C

E ::= expressions

P ::= first-order logic plus arithmetic etc.

There is also a notation for procedures, which we do not consider here.

Only commands can be specified, and not expressions. There are two specification constructs. The notation x⃗ : [P, P′] is a specification of a command which with precondition P of the state, results in postcondition P′, but only altering variables in the ‘frame’ x⃗.

The alternation construct, if … [] … fi, is a nondeterministic choice between commands C_{i} whose ‘guard’ P_{i} is true. If none of the P_{i} are true then the command is unsatisfiable.

The declaration of logical constants con x : σ • C, is not program code, but an abbreviation introduced during development that must ultimately be refined into code.

Annotations – ‘assumptions’ and ‘coercions’ – are defined as commands using specifications (unlike in Remark 4.3.22). The assumption {pre} = ⟨⟩ : [pre, ⊤], and the coercion [post] = ⟨⟩ : [⊤, post]. In fact, abort and skip can be defined using specifications.

The distinction between imperative and functional languages seems (theoretically) irrelevant for our study of refinement. We can translate the imperative features into our calculus in the style of Idealised Algol. For example, we add a primitive type state, and define a translation ( )^{∘} of terms into our calculus:

                       (com)^{∘} = state → state

(x := E)^{∘} = assign(x, E^{∘})

(var x : σ • C)^{∘} = new(λx : σ.C^{∘})

In order to avoid considerations of nontermination, we could assume that all recursion is terminating, and so could be encoded using primitive recursion (say).

We are more interested here though in translating the specificational features into our calculus. Logical constants can be translated as:

(con x : σ • C)^{∘} = let x : σ in C^{∘}

Although the imperative refinement calculus does not have choice for expressions, most other authors do, and it is useful in our translation. We can define this as

              (r[]r′)^{∘} = let b : boolin (if b then r else r′)

The connection between propositions and booleans is usually not satisfactorily accounted for. It is commonplace (and useful) to write propositions in place of booleans, but not explained how they might ultimately be refined into booleans. We do not study this, but can associate boolean term t with proposition P, by using sum types and asserting that t : 1 | P + 1 | ¬P. We use the notation if P then P → r else ¬P → r′ to mean

case b : 1|P + 1|¬P of λz : 1|P.r, λz : 1|¬P.r

so may use the assumptions P, ¬P when reasoning about r and r′ respectively. We will write propositions with this convention.

(if b → r [] b′ → r′ fi)^{∘} =

if (b and b′) then r[]r′

                           elseif b then r
                           elseif b′ then r′

                           else ?_{(z:τ) b=true ∨ b′=true}

The final branch is intended to mean that if both b and b′ are false, then the term is unsatisfiable. If we had put ?_{(z:τ) ⊥} then this would force the whole term to be unsatisfiable.

Logically, pre and postconditions are just a particular form of property. We define the frame proposition Fr_{Γ}(s, s′) to mean that states s and s′ can only differ on the variables in Γ. We use the propositions pre and post as properties over state. Let Fr_{Γ}(s, s′) ≡ ∀x : var . sx ≠ s′x ⊃ x ∈ Γ.

(Γ : [pre, post])^{∘} = ?_{Π_{s:pre}(s′:post)Fr_{Γ}(s,s′)}

We can show that

{pre}^{∘} = λs : pre.s

Without some form of annotation, coercions do not have such a neat representation though.

We assume a Hoare logic of commands is given schematically. For example, for all propositions P, terms e : σ and variables x : σ, we have assign_{σ}(x, e) :

P [e/x] → P. With a sufficiently powerful type theory, and object level substitutions, this definition could be internalised.

Morgan introduces a large number of refinement laws, though they are not arranged into a complete system of refinement rules, and there is no logic of refinement terms. We now consider two laws presented in [Mor94].

Absorb assumption {pre′}; (Γ : [pre, post]) = Γ : [pre′ ∧ pre, post]

This does not hold in our calculus. To see why, observe that in general we do not have

(λx : ϕ|P.x); ?_{Π_{x:ϕ}ψ} = ?_{Π_{x:ϕ|P} ψ}

since although we might be able to satisfy ψ for every ϕ such that P, so the second expression is satisfiable, we might not be able to do this for every ϕ, so the first expression is unsatisfiable. Now, the translation of the left hand side of the law is

(λs : pre′.s); ?_{Π_{s:pre}(s′:post)Fr_{Γ}(s,s′)}

The failure of this equivalence does not mean that in our calculus it is impossible to use external assumptions when reasoning about specifications. Indeed, this nonlocality of satisfiability leads to complications. In order to understand one subspecification, the entire system needs to be considered. Rather, we believe that satisfiability of specifications should be local, and that assumptions be made contextually – that is, in an explicit global or local context. It seems unlikely that an expression of the form {pre′}; Γ : [pre, post] would actually arise during refinement anyway.

The contrasting status of the law in λ_{⊑} and [Mor94] is indicative of the difference between underdeterminism and nondeterminism (see Remark 5.2.7).

Alternation If pre ⊃ P_{1} ∨ P_{2}, then Γ : [pre, post] refines to

\begin{aligned}if & P_{1} → Γ : [pre ∧ P_{1}, post] [] \\ & P_{2} → Γ : [pre ∧ P_{2}, post] fi \end{aligned}

Let r_{1} ≡ ?_{Π_{s:pre∧P_{1}}(s′:post)Fr_{Γ}(s,s′)} and r_{2} ≡ ?_{Π_{s:pre∧P_{2}}(s′:post)Fr_{Γ}(s,s′)}. We show that

r ≡ ?_{Π_{s:pre}(s′:post)Fr_{Γ}(s,s′)}
⊑ if P_{1} ∧ P_{2} then r_{1}[]r_{2}

                         elseif P_{1} then r_{1}
                         elseif P_{2} then r_{2}

else ?_{(c:com) P_{1}∨P_{2}}

We need two auxiliary results 1. r ⊑ if P then P → r else ¬P → r

2. If r ⊑ r_{1} and r ⊑ r_{2}, then r ⊑ r_{1}[]r_{2}

So by 1,

r ⊑ if P_{1} ∧ P_{2} then P_{1} ∧ P_{2} → r else ¬(P_{1} ∧ P_{2}) → r

and by 1 again, refine the second branch to get if P_{1} ∧ P_{2} then P_{1} ∧ P_{2} → r elseif ¬(P_{1} ∧ P_{2}) ∧ P_{1} then P_{1} → r else ¬(P_{1} ∧ P_{2}) ∧ ¬P_{1} → r

and then if P_{1} ∧ P_{2} then P_{1} ∧ P_{2} → r elseif ¬(P_{1} ∧ P_{2}) ∧ P_{1} then P_{1} → r elseif P_{2} then¬(P_{1} ∧ P_{2}) ∧ ¬P_{1} → r else ¬(¬(P_{1} ∧ P_{2}) ∧ ¬P_{1}) → r

which refines to if P_{1} ∧ P_{2} then P_{1} ∧ P_{2} → r elseif ¬(P_{1} ∧ P_{2}) ∧ P_{1} then P_{1} → r else ¬(P_{1} ∧ P_{2}) ∧ ¬P_{1} → r

and then if P_{1} ∧ P_{2} then P_{1} ∧ P_{2} → r

elseif P_{1} then P_{1} → r
elseif P_{2} then P_{2} → r

else ¬(P_{1} ∨ P_{2}) → r

Now P_{1} ⊢ r ⊑ r_{1} and P_{2} ⊢ r ⊑ r_{2}, so by 2, P_{1} ∧ P_{2} ⊢ r ⊑ r_{1}[]r_{2}. For the final branch, if ¬(P_{1} ∨ P_{2}) then r is unsatisfiable so we refine it to ?_{(c:com) P_{1}∨P_{2}}. Hence the term refines to

if P_{1} ∧ P_{2} then r_{1}[]r_{2}

elseif P_{1} then r_{1}
elseif P_{2} then r_{2}

else ?_{(c:com)P_{1}∨P_{2}}

5.5 Metatheory

In this section we prove a number of proof-theoretic results about the refinement calculus. Besides being used in the completeness proof of the next section, these results are inherently interesting and provide insight into the nature of refinement.

We extend the results of Section 3.3 in which we showed that the simple refinement relation of λ_{?} could be factored into ‘coding’ and equality. The main idea is that a refinement can be factored into a simple form of ‘non-logical’ refinement, and a logical equality. These relations can be seen as generalisations of the simply-typed refinement relation in λ_{?} and the logical equality in λ_{(:)} respectively. Mirroring the results for λ_{?}, the simple refinement can, in turn, be factored into coding and ‘coercion’.

Before proving the factorisation itself, we use the characterisation of logical equality to show that refinement typings can be proven in a standard way. Such so-called generation lemmas are useful for metatheoretic reasoning about judgements.

The idea of the lemma is that if a term satisfies a specification, then we should be able to prove this by induction on the structure of the term. For example, if the pair ⟨r, r′⟩ satisfies some specification χ, then we should be able to prove something about r, something about r′ and conclude from this that the pair satisfies χ. Formally, we would like so say that there are refinement types ϕ and ψ such that r : ϕ, r′ : ψ and that χ ⊑ ϕ × ψ (or, in general, that x : ϕ ⊢ r′ : ψ and χ ⊑ Σ_{x:ϕ}ψ). This is often the case. However, it is sometimes possible to directly infer that a term satisfies a specification, if this is taken as an axiom, for example. In fact, it is the three Refinement Type Introduction rules which break the structural form of refinement typing, in the sense that the inferred refinement type need have no relationship to the term. Thus we formulate the Generation Lemma to account for these two possibilities.

The proof exploits the fact that Refinement Elimination is only useful in combination with the subset of rules corresponding to a relation ‘logical eta’ which we will define, and that this can be eliminated.

Lemma 5.5.1 (Generation) If Γ ⊢ r : χ then either this is derived using a rule of Refinement Type Introduction, followed by Weakening, or it is derived on the structure of r, as follows:

1. If Γ ⊢ x : χ then there exists a ϕ such that Γ ≡ Γ_{1}, x : ϕ, Γ_{2} and Γ_{1} ⊢ χ ⊑ ϕ.

2. If Γ ⊢ k(r_{1}, …, r_{n}) : χ then there is an axiom Γ ⊢ k : ϕ_{1}, …, ϕ_{n} → ψ such that Γ ⊢ r_{i} : ϕ_{i} (i = 1, …, n) and Γ ⊢ χ ⊑ ψ.

3. If Γ ⊢ ∗ : χ then there exists Γ ⊢ P wf such that Γ ⊢ P and Γ ⊢ χ = (z : 1)P.

4. If Γ ⊢ ⟨r, r′⟩ : χ then there exists Γ ⊢ ϕ, ψ wf such that Γ ⊢ r : ϕ, Γ, x : ϕ ⊢ r′ : ψ and Γ ⊢ χ ⊑ Σ_{x:ϕ}ψ.

5. If Γ ⊢ λx : ϕ.r : χ then there exists Γ, x : ϕ ⊢ P wf and Γ, x : ϕ, P ⊢ ψ wf such that Γ, x : ϕ, P ⊢ r : ψ and Γ ⊢ χ ⊑ Π_{x:ϕ|P} ψ.

6. If Γ ⊢ ?_{ϕ} : χ then Γ ⊢ χ ⊑ ϕ.

7. If Γ ⊢ π_{1}(r) : χ then there exists Γ ⊢ ψ wf such that Γ ⊢ r : χ × ψ.

8. If Γ ⊢ π_{2}(r) : χ then there exists Γ ⊢ ϕ wf such that Γ ⊢ r : ϕ × χ.

9. If Γ ⊢ rr′ : χ then there exists Γ ⊢ ϕ wf and Γ ⊢ ψ wf such that Γ ⊢ r : ϕ → χ and Γ ⊢ r′ : ϕ.

10. If Γ ⊢ let x : ϕ be r in r′ : χ then there exists Γ, x : ϕ ⊢ P wf such that Γ ⊢ r : (x : ϕ)P and Γ, x : ϕ, P ⊢ r′ : χ.

Proof: We first show that we can eliminate ‘nonessential’ uses of the Refinement Elimination rule. This rule is only useful in combination with the refinements given by the logical equality rules of Figure 5.13. We induct over these rules to show that in each case we can replace the use of the rule followed by a refinement elimination with a single derived (or basic) rule whose hypotheses and conclusions are all refinement typings (or well-formedness conditions). For example, in place of using Function Equations (ξ) in

\begin{matrix}Γ, x : ϕ ⊢ P wf & Γ, x : ϕ, P ⊢ t′ : ψ \\ Γ ⊢ λx : (x : ϕ)P.t =_{Π_{x:ϕ|P} ψ} λx : ϕ.t′ \end{matrix} ^{Func. Eqs. (ξ)}

Γ ⊢ λx : ϕ.t′ : Π_{x:ϕ|P} ψ Ref. Elim.

we have the rule

\begin{matrix}Γ, x : ϕ ⊢ P wf & Γ, x : ϕ, P ⊢ t′ : ψ \\ Γ ⊢ λx : ϕ.t′ : Π_{x:ϕ|P} ψ \end{matrix}

Note that we are not eliminating uses of Refinement Elimination here. This derived rule still makes use of it. The rule Let Eta would be used in

Γ ⊢ r : ϕ

Γ ⊢ let x : ϕ be r in x =_{ϕ} r

Let Eta

Γ ⊢ let x : ϕ be r in x : ϕ

Ref. Elim.

but this can be proven directly as

Γ ⊢ r : ϕ Γ, x : ϕ ⊢ x : ϕ
Γ ⊢ let x : ϕ be r in x : ϕ

Similar analyses hold for the other rules of Figure 5.13.

Thus we can assume, without loss of generality, that if Γ ⊢ r : ϕ is provable and Ref.Type Intro. is not used, then it has been inferred from the refinement typing rules of Figure 5.4, together with the derived rules above and Weakening from Figure 5.5. Now we need just show that each of these rules preserves the conditions of the lemma, in the sense that if the hypotheses of a rule can be derived in the standard way (described by the lemma), then the conclusion can be inferred in the standard way. In fact, this is immediate for the refinement typing rules of Figure 5.4 and the derived rules. We need just check the case of Weakening. For example, suppose

\begin{matrix}Γ ⊢ ⟨r, r′⟩ : χ′ & Γ ⊢ χ ⊑ χ′ \\ Γ ⊢ ⟨r, r′⟩ : χ \end{matrix}

By the inductive hypothesis, either there exists ϕ, ψ such that Γ ⊢ r : ϕ, Γ, x : ϕ ⊢ r′ : ψ and Γ ⊢ χ′ ⊑ Σ_{x:ϕ}ψ, and so Γ ⊢ χ ⊑ Σ_{x:ϕ}ψ.

Cases 7-9 show that nondependent hypotheses suffice for the elimination rules. Then, from the point of view of completeness, the restriction on the rules for Product Terms and Function Terms in Figure 5.4 is not a problem.

We remark that the proof of Lemma 5.5.1 does not depend on the factorisation result which we give below.

Lemma 5.5.2 (Subject Refinement) If Γ ⊢ r : (x : ϕ)P and Γ ⊢ r ⊑_{ϕ} r′, then Γ ⊢ r′ : (x : ϕ)P.

Proof: We use induction over Γ ⊢ r ⊑_{ϕ} r′ and the Generation Lemma. For example, if Γ ⊢ ?_{ψ×ψ′} : (x : ϕ)P then, either this follows from Ref. Type Intro. followed by Weakening (in which case P must be a predicate symbol and we can use Ref.Type Intro.) or Γ ⊢ (x : ϕ)P ⊑ ψ × ψ′. So, Γ ⊢ ?_{ψ×ψ′} ⊑_{ψ×ψ′} ⟨?_{ψ},?_{ψ′}⟩ and Γ ⊢ ⟨?_{ψ},?_{ψ′}⟩ : ψ × ψ′ so by Weakening, Γ ⊢ ⟨?_{ψ},?_{ψ′}⟩ : (x : ϕ)P.

We want to split a refinement r ⊑_{ϕ} r′ into an equality at ϕ and some form of ‘nonlogical’ refinement independent of any refinement type. However, it is not immediately clear how make such a definition, because we have only defined refinement at specific refinement types. Some refinements are provable at every refinement type (of the term to be refined), though, and this will be our definition. For example, ?_{even→nat} refines to λn : even.n at every refinement type of ?_{even→nat}.

Definition 5.5.3 We define a form of untyped refinement, ⊑, between terms. We say that Γ ⊢ r ⊑ r′ holds when for all provable Γ ⊢ ϕ wf, if Γ ⊢ r : ϕ is provable then so is Γ ⊢ r ⊑_{ϕ} r′.

In order to prove the factorisation theorem we need the fact that the axioms can all be factorised. The easiest way of doing this is to assume that the axioms are in λ_{(:)} (which we assumed in Definition 5.2.3).

Theorem 5.5.4 (Factorisation) If Γ ⊢ r ⊑_{ϕ} r′ then there exists a term r′′ such that Γ ⊢ r ⊑ r′′ and Γ ⊢ r′′ =_{ϕ} r′, and a term r′′′ such that Γ ⊢ r =_{ϕ} r′′′ and Γ ⊢ r′′′ ⊑ r′.

Proof: We give a sketch of the proof. The central idea is to partition the rules into what we call logical eta, simple refinement, and computation, by defining relations =_{ϕ}^{η}, ⊑^{s} and =^{βlet}, given in Figures 5.13, 5.14 and 5.15 respectively. Note that the decomposition rules (top-down refinement rules of Figure 5.9) are derivable for ⊑^{s}.

To a certain extent, the definitions of these relations are arbitrarily made to get the proof to go through. For example, we include the Eta rule in ⊑^{s} simply because it is not clear whether it commutes with the other rules in ⊑^{s} (this fact being needed for the proof).

Then, by combining computation with simple refinement and logical eta, respectively, we get nonlogical refinement and logical equality. Specifically, we define =′_{ϕ} as the reflexive symmetric transitive closure of =_{ϕ}^{η} and =^{βlet}; and define ⊑′ as the reflexive transitive closure of ⊑^{s} and =^{βlet}. (We will show that =′_{ϕ} is contained in =_{ϕ} and ⊑′ is contained in ⊑.)

1. Refinement rules are of two kinds: axioms, that is, those whose hypotheses do not contain refinements; and the congruence rules. Prove that all axioms factor into =′_{ϕ}; ⊑′ and ⊑′; =′_{ϕ}, and that congruence rules and Substitution preserve factorisations. For Disjunction, if assuming P the refinement factors through r_{P} and assuming Q it factors through r_{Q}, then assuming P ∨ Q, it factors through P → r_{P} [] Q → r_{Q} (defined using annotations and choice). The only rules which are not exclusively =^{βlet}, =_{ϕ}^{η} or ⊑^{s} are the stubs refinement rule, and the three ‘complex’ congruence rules.

2. Induct over R ∈⊑^{s} to show that

R; =′_{ϕ} ⇒ =′_{ϕ}; ⊑^{s}
=′_{ϕ}; R ⇒ ⊑^{s}; =′_{ϕ}

3. Since this clearly holds for R ∈=^{βlet} we conclude that ⊑′ (on ϕ) commutes with =_{ϕ}, and hence that ⊑_{ϕ} factors into ⊑′ and =′_{ϕ}.

4. Show that =′_{ϕ} ⊆ =_{ϕ}. Hence, ⊑_{ϕ} factorises into ⊑′ and =_{ϕ}.

Corollary 5.5.5 The refinement relation, ⊑_{ϕ}, factorises into ⊑ and =_{ϕ}; that is, if Γ ⊢ r ⊑_{ϕ} r′ then

Proof: By Theorem 5.5.4, ⊑_{ϕ} factorises into ⊑′ and =_{ϕ}. The result follows on using the Generation Lemma to show that ⊑′ ⊆ ⊑.

Corollary 5.5.6 If Γ ⊢ r ⊑_{ϕ} t, then there exists a term t′ such that Γ ⊢ r ⊑ t′ and Γ ⊢ t′ =_{ϕ} t.

Proof: Suppose Γ ⊢ r ⊑_{ϕ} t. By Theorem 5.5.4, there exists a term r′ such that Γ ⊢ r ⊑ r′ and Γ ⊢ r′ =_{ϕ} t. Now, it is not necessarily the case that r′ is determined. However, if we construct the term t′ by replacing each stub in r′ with a determined refinement, then Γ ⊢ r ⊑ r′ ⊑ t′ and Γ ⊢ t′ =_{ϕ} t.

We now generalise the canonical forms lemma of Chapter 3 and show that each term has a canonical form to which it is equal at all its refinement types.

Lemma 5.5.7 (Canonical Forms) For all terms in context Γ ⊢ r, there exists a context x_{1} : ϕ_{1}, …, x_{n} : ϕ_{n} and a determined term Γ, x_{1} : ϕ_{1}, …, x_{n} : ϕ_{n} ⊢ t wf such that each x_{i} appears exactly once in t, and

Γ ⊢ (let x_{1} : ϕ_{1}, …, x_{n} : ϕ_{n} be ?_{ϕ_{1}}, …,?_{ϕ_{n}} in t) = r

Proof: Use =^{βlet} rules of Figure 5.15 to move the underdeterminism outwards.

We have shown that an arbitrary refinement, r ⊑_{ϕ} r′, factorises into ⊑ (or ⊑′) and =_{ϕ}. We now show that further factorisations can be made when refinement is to a determined term. We defined ⊑′ as the reflexive transitive closure of ⊑^{s} and =^{βlet}. In fact, it factorises in the following way:

Lemma 5.5.8 If Γ ⊢ r ⊑′ t then there exists a t′ such that Γ ⊢ r ⊑^{s} t′ and Γ ⊢ t′ =^{βlet} t.

Proof: We can show that =^{βlet} commutes with ⊑^{s}, in the direction: if Γ ⊢ r =^{βlet}; ⊑^{s} r′ then Γ ⊢ r ⊑^{s}; =^{βlet} r′, and the result follows.

Function Equations (η) (Fig. 5.6) Let Eta (Fig. 5.7)

Eta

Axioms  Axioms of the form Γ ⊢ t =_{ϕ} t′

Weakening  \begin{matrix}Γ ⊢ r =_{ϕ′} r′ & Γ ⊢ ϕ ⊑ ϕ′ \\ Γ ⊢ r =_{ϕ} r′ \end{matrix}

Strengthening  \begin{matrix}Γ ⊢ r =_{ϕ} r′ & Γ ⊢ r : (x : ϕ)P \\ Γ ⊢ r =_{(x:ϕ)P} r′ \end{matrix}

{Γ, x : ϕ, P ⊢ t =_{ψ} t′ \atop Γ ⊢ λx : ϕ|P .t =_{Π_{x:ϕ|P} ψ} λx : ϕ.t′}

{Γ, x : ϕ ⊢ r =_{ψ} r′ \atop Γ ⊢ λx : ϕ.r =_{Π_{x:ϕ}ψ} λx : ϕ.r′}

Γ, x : ϕ ⊢ P wf Γ ⊢ r_{1} =_{(x:ϕ)P} r′_{1} Γ, x : ϕ, P ⊢ r_{2} =_{ψ} r′_{2}

Γ ⊢ let x : (x : ϕ)P be r_{1} in r_{2} =_{ψ} let x : ϕ be r′_{1} in r′_{2}

(x ∉ F V (ψ))

congruence rules for constants, pairs, applications, projections Congruence

Figure 5.13: Logical Eta: =_{η}

Coding  {Γ ⊢ t : ϕ \atop Γ ⊢ ?_{ϕ} ⊑^{s} t}
Γ ⊢ r′ : ψ Γ ⊢ r : ϕ Γ ⊢ r′ ⊑^{s} let x : ϕ be r in r′ (x ∉ F V (r′)) Let Weakening
Stubs  {Γ ⊢ ϕ ⊑ ϕ′ \atop Γ ⊢ ?_{ϕ} ⊑^{s} ?_{ϕ′}}
Γ ⊢ r : ϕ
Γ ⊢ let x : ϕ be r in x ⊑^{s} r

Eta

{Γ ⊢ r ⊑^{s} r′ \atop Γ ⊢ C[r] ⊑^{s} C[r′]}

{Γ, x : ϕ, P ⊢ r ⊑^{s} r′ \atop Γ ⊢ λx : ϕ|P .r ⊑^{s} λx : ϕ.r′}

Γ ⊢ ϕ ⊑ ϕ′ Γ ⊢ r_{1} ⊑^{s} r′_{1}

Γ ⊢ let x : ϕ be r_{1} in r_{2} ⊑^{s} let x : ϕ′ be r′_{1} in r_{2}

Γ ⊢ ϕ ⊑ ϕ′ Γ, x : ϕ ⊢ r_{2} ⊑^{s} r′_{2} Γ ⊢ r_{1} : ϕ′

Γ ⊢ let x : ϕ be r_{1} in r_{2} ⊑^{s} let x : ϕ′ be r_{1} in r′_{2} Congruence

Figure 5.14: Simple Refinement: ⊑^{s}

{Γ ⊢ ϕ × ψ wf \atop Γ ⊢ ?_{ϕ×ψ} = ⟨?_{ϕ},?_{ψ}⟩}

{Γ ⊢ Π_{x:ϕ}ψ wf \atop Γ ⊢ ?_{Π_{x:ϕ}ψ} = λx : ϕ.?_{ψ}}

Decomposition

Function Equations (β) (Fig. 5.6) Product Equations (β) (Fig. 5.6)

Beta

Product Equations (η) (Fig. 5.6) Unit Equation (Fig. 5.6)

Eta

Let Equalities Figures 5.7 and 5.8 (except Let Eta)

{Γ, x : ϕ ⊢ r = r′ \atop Γ ⊢ λx : ϕ.r = λx : ϕ.r′}

Γ ⊢ r_{1} = r′_{1} Γ, x : ϕ ⊢ r_{2} = r′_{2}

Γ ⊢ let x : ϕ be r_{1} in r_{2} = let x : ϕ be r′_{1} in r′_{2} Congruence

Figure 5.15: Computation: =_{βlet}

In Chapter 3, we defined a coding relation, ⇝, and we now extend the definition to λ_{⊑} in the obvious way.

Definition 5.5.9 We define the coding relation on well-formed terms, Γ ⊢ r ⇝ r′, as the reflexive, transitive, congruence closure of the following onestep relation:

{Γ ⊢ t : ϕ \atop Γ ⊢ ?_{ϕ} ⇝ t}

It is also possible to refine terms simply by weakening the refinement types on binders. We define a relation, ≤, for this notion of ‘coercion’.

Definition 5.5.10 We define the coercion relation on well-formed terms, Γ ⊢ r ≤ r′, to be the reflexive, transitive, congruence closure of the following one-step relation:

\begin{matrix}Γ, x : ϕ ⊢ r wf & Γ, x : ϕ ⊢ P wf \\ Γ ⊢ λx : ϕ|P .r ≤ λx : ϕ.r \end{matrix}

Γ ⊢ ϕ ⊑ ϕ′ Γ ⊢ r : ϕ′ Γ, x : ϕ ⊢ r′ wf
Γ ⊢ let x : ϕ be r in r′ ≤ let x : ϕ′ be r in r′

Now, in Chapter 3 we showed that refinement to a determined term in λ_{?} could be factored into coding and equality (Lemma 3.3.2). The generalisation of this lemma to λ_{⊑} is:

Lemma 5.5.11 If Γ ⊢ r ⊑^{s} t then Γ ⊢ r ⇝; ≤ t and Γ ⊢ r ≤; ⇝ t.

Proof: We first show that all rules of ⊑^{s} factor into ⇝ and ≤. Clearly ⇝ and ≤ commute. All we need show, then, is that the ⊑^{s} rules of Let Weakening and Eta can be eliminated:

(Let Weakening) Suppose r′ ⊑^{s} let x : ϕ be r in r′ ⇝ let x : ϕ be t in t′. Then, clearly, r′ ⇝ t′[t/x].

(Eta) Suppose let x : ϕ be r in x ⊑^{s} r ⇝ t. Then let x : ϕ be r in x ⇝

     let x : ϕ be t in x.

We will use the following consequences in the completeness proof:

Lemma 5.5.12 1. If Γ ⊢ let z : ϕ be r in r′ ⊑′ t′ then there exists a term Γ ⊢ t : ϕ such that Γ ⊢ r ⊑′ t and Γ ⊢ r′[t/z] ⊑′ t′.

2. If Γ ⊢ let z : ϕ be r in r′ ⊑_{ψ} t′ then there exists a term Γ ⊢ t : ϕ such that Γ ⊢ r ⊑_{ϕ} t and Γ ⊢ r′[t/z] ⊑_{ψ} t′.

Proof:

1. Suppose Γ ⊢ let z : ϕ be r in r′ ⊑′ t′. Then, by Lemma 5.5.8, there exists a term t′′ such that Γ ⊢ let z : ϕ be r in r′ ⊑^{s} t′′, and Γ ⊢ t′′ =^{βlet} t′, so by Lemma 5.5.11, Γ ⊢ let z : ϕ be r in r′ ⇝; ≤ t′′, that is, there exists determined u, u′ such that Γ ⊢ r ⇝ u and Γ ⊢ r′ ⇝ u′ and let z : ϕ be u in u′ ≤ t′′. Hence, Γ ⊢ r ⊑′ u and Γ ⊢ r′[u/z] ⊑′ u′[u/z] ⊑′ t′′ =^{βlet} t′.

2. If Γ ⊢ let z : ϕ be r in r′ ⊑_{ψ} t′, then by Theorem 5.5.4, there exists a t′′ such that Γ ⊢ let z : ϕ be r in r′ ⊑′ t′′ =_{ψ} t′. By part 1 of this lemma, there exists a term Γ ⊢ t : ϕ such that Γ ⊢ r ⊑′ t, so Γ ⊢ r ⊑_{ϕ} t, and Γ ⊢ r′[t/z] ⊑′ t′′, so Γ ⊢ r′[t/z] ⊑_{ψ} t′.

Lemma 5.5.13 (Completeness of refinement to programs) If Γ ⊢ t : ϕ then Γ ⊢ ?_{ϕ} ⊑_{ϕ} t.

Proof: This follows directly using Substitution.

Definition 5.5.14 For Γ ⊢ r, r′ wf, define r ≲_{ϕ}^{Γ} r′ to mean: for all Γ′ ⊇ Γ, for all determined t′, if Γ′ ⊢ r′ ⊑_{ϕ} t′, then Γ′ ⊢ r ⊑_{ϕ} t′.

Lemma 5.5.15 (Refinement Mappings) If

let x : ϕ in t_{1} ≲_{χ}^{Γ} let y : ψ in t_{2}

then there exists Γ, y : ψ ⊢ t : ϕ, such that Γ, y : ψ ⊢ t_{1}[t/x] =_{χ} t_{2}.

Proof: Since Γ, y : ψ ⊢ let y : ψ in t_{1} ⊑_{χ} t_{2}[y], by the definition of ≲_{χ}^{Γ} we have Γ, y : ψ ⊢ let x : ϕ in t_{1} ⊑_{χ} t_{2}[y]. By Lemma 5.5.12 (2) this means there exists a term Γ, y : ψ ⊢ t : ϕ such that Γ, y : ψ ⊢ t_{1}[t/x] =_{χ} t_{2}.

5.6 Models

We first motivate the semantics for the λ_{⊑}-calculus in Section 5.6.1 before giving the details in Section 5.6.2, and proving soundness and completeness.

5.6.1 Discussion

We discuss the properties we would like the semantics of the refinement calculus to have and give intuitive meanings to refinement typing and refinement in the λ_{⊑}-calculus. It is our intention to model the calculus using a form of Henkin interpretation, and so enable comparison with the models of the subcalculi.

In Chapter 3, a refinement term r was thought of as denoting a set of values, or realizers, corresponding to the programs which satisfy the specification. In Chapter 4, determined terms t were also seen as denoting sets of total realizers. Perhaps unexpectedly, we cannot think of terms in the full refinement calculus as denoting sets.

To see this, we must consider what the meaning of the refinement typing, r : ϕ, should be. A first approximation to what this means is ‘every realizer of r is in ϕ’. This would be wrong, however, as we do not want λn : even.n : nat → nat to be true, yet every realizer of λn : even.n is certainly in nat → nat. The problem is that the interpretation of λn : even.n as a set is losing the information that its realizers are only determined up to even → nat.

This is analogous to the distinction between even → even and (f : nat → nat) ∀n : even . Even(fn). Although these refinement types correspond to the same sets of total terms, (we could say they have the same ‘extension’), they represent different equalities. We can recast this example as the distinction between the refinement terms λn : even.n and ?_{(f:nat→nat) ∀x:nat . Even(x) ⊃ fx=x}.

The problem is that our interpretation of refinement types uses pers, rather than just types. Somehow we need to involve pers in the interpretation of terms as well.

In Chapter 4, we said that t : ϕ is true when all realizers of t are equal at ϕ. This will certainly prevent λn : even.n having refinement type nat → nat, but then ?_{nat→nat} : nat → nat will not be true either.

The solution is to think of refinement terms as sets of equivalence classes of some per. In the case of λn : even.n, we should interpret this as a single class in the per even → nat. The term ?_{(f:nat→nat) ∀n:even.fn=n} is interpreted as all the classes in the per (f : nat → nat) ∀n : even.fn = n.

However, since we interpret preterms, it is not immediately obvious which per the equivalence classes should be from, but since a set of equivalence classes of some per is itself just a per, we simply interpret refinement terms as pers.

For example, the refinement term ?_{ϕ} will be interpreted as the same per as the refinement type ϕ. Then we can succinctly express the semantic meaning of refinement typing: r : ϕ is true when (the meaning of) r is a subper of (the meaning of) ϕ.

We will show in the next section that determined terms (i.e. terms in the λ_{(:)}-calculus) are interpreted as a single equivalence class. Thus we can regain the set-theoretic intuition that an underdetermined term r corresponds to a set of realizers — now the realizers can be thought of as determined terms, corresponding to the equivalence classes of r. This means that r : ϕ can be thought of as “for all r ⊑_{ϕ} t, we have t : ϕ”. Semantically, the final stage of a refinement is a single equivalence class.

Given this intuition of a refinement term as a per, how are we to think of refinement? Semantically, there are two forms of refinement: reducing the number of classes (restriction) and reducing the size of the classes (quotienting). For example, ?_{even→nat} can be quotiented to ?_{nat→nat}, and restricted to λn : even.n. Both operations give subpers.

In practice, refinement is more likely to consist of progressive restrictions (in this semantic sense) on a specification towards a program. Non-discrete equivalences can only arise through refinement types on abstractions. Quotienting would correspond to a relaxing of these assumptions, thus increasing the domain of definition.

We think of refinement at ϕ, then, as being a combination of reducing the number of ϕ-classes, and of making the classes finer. These two relations can be combined by saying that r ⊑_{ϕ} r′ is true when (using capitals for the meaning of expressions) R′ is contained in the Φ-closure of R, that is, R′ is a subper of Φ; R; Φ. In fact, we also require that R is a subper of Φ.

For example, λn : even.n ⊑_{nat→nat} λn : even.n is not true, since the single class denoted by λn : even.n (in even → nat) is not equal to itself at nat → nat. As pointed out above, λn : even.n does not have refinement type nat → nat. Similarly, we do not have ?_{nat→nat} ⊑_{nat→nat} λn : even.n.

Since the goal of refinement is to reach a term which represents a single equivalence class, syntactically it culminates in a determined term, and not necessarily as a term in λ^{×→}. Hence, from a semantic standpoint, we are consistent in continuing to use t as a metavariable for determined terms.

5.6.2 λ_{⊑}-Henkin Models

As suggested for λ_{?} in Remark 3.4.12, there are two possible approaches to giving a semantics. One possibility is to interpret Γ ⊢ r as a per. Here we interpret the calculus in Henkin models with the additional structure introduced to model the subcalculi in Sections 3.4 and 4.5, namely factoring and per structure. The only difference is that the factoring condition must be strengthened to account for logical structure.

We follow the pattern of previous chapters, by first giving the interpretation of pre-expressions, and then defining when an interpretation of a signature models an axiom system.

We define the notion of Γ-environment as in Chapter 4, and write η ⊨^{A} Γ when η is a Γ-environment in λ_{⊑}-Henkin interpretation, A. Then the pre-expression in context, Γ ⊢ U, is interpreted in a Γ-environment, η. We interpret pre-expressions so as to avoid the need for establishing coherence (as for the λ_{(:)}-calculus).

Since the basic data of λ_{⊑}-axiom systems is the same as for first-order λ^{×→}- and λ_{(:)}-axiom systems, we give the interpretation of pre-expressions in a firstorder λ^{×→}-Henkin interpretation in Γ-environment, η, in Figures 5.16 to 5.18. Figure 5.16 gives the interpretation of refinement types as pers over the set corresponding to the underlying type. This is the same interpretation for the λ_{(:)}- calculus but we repeat it here. Figure 5.17 gives the interpretation of refinement terms, also as pers. For a an element of per R, we use the notation {a}_{R} for the singleton per consisting of the class of a.

As for λ_{(:)}-models, the soundness theorem for λ_{⊑} will imply that the choice of a in the semantics of abstractions is not important; similarly for the other binding expressions.

We define some of the pers as sets of pairs. We explain the cases for abstractions and pairs. The abstraction, λx : ϕ.r, denotes a per which relates two functions if for all arguments related at ϕ, the results are related by r. The letexpression, let x : ϕ be r in r′, is similar, but relates two individuals if there exists a pair related by r. Since r : ϕ, soundness implies that the choice does not matter.

The interpretation of pre-propositions, Γ ⊢ P, is given in Figure 5.18 as the set of Γ-environments in which P is true. It is convenient to write [[Γ ⊢ P]](η) for the truth or falsehood of η ∈ [[Γ ⊢ P]].

Having given the interpretation of pre-expressions we can make the following definitions.

Definition 5.6.1 Let A be a first-order λ^{×→}-Henkin interpretation. We say that A satisfies the logical factoring condition when for each f′ ∈ A^{τ→τ′}, f ∈ A^{σ→τ′}, such that f′ ∈ [[ψ → ψ′]]^{A}, f ∈ [[ϕ → ψ′]]^{A}, we require that if there exists h ∈ A^{τ} → A^{σ} such that f̄′ = h; f̄ then there exists g ∈ [[ψ → ϕ]]^{A} such that f̄′ = ḡ; f̄.

Definition 5.6.2 Let Sg = ⟨G, K, F⟩ be a λ_{⊑}-signature. A λ_{⊑}-Henkin interpretation of Sg is a first-order λ^{×→}-Henkin interpretation of Sg which satisfies the

a [[Γ ⊢ 1]](η) a′ ⟺ a, a′ ∈ 1^{A}
[[Γ ⊢ ϕ]] = R [[Γ, x : ϕ ⊢ ψ]] = S

a [[Γ ⊢ Σ_{x:ϕ}ψ]](η) a′ ⟺ Proj_{1}^{σ,τ}(a) R(η) Proj_{1}^{σ,τ}(a′) and Proj_{2}^{σ,τ}(a) S⟨η, Proj_{1}^{σ,τ}(a)⟩ Proj_{2}^{σ,τ}(a′)

[[Γ ⊢ ϕ]] = R [[Γ, x : ϕ ⊢ ψ]] = S

f [[Γ ⊢ Π_{x:ϕ}ψ]](η) f′ ⟺ for all a R(η) a′, App(f, a) S⟨η, a⟩ App(f′, a′)

\begin{matrix}[[Γ ⊢ ϕ]] = R & [[Γ, x : ϕ ⊢ P]] = A \\ a [[Γ ⊢ (x : ϕ)P]](η) a′ ⟺ a R(η) a′, ⟨η, a⟩ ∈ A, ⟨η, a′⟩ ∈ A \end{matrix}

a [[Γ ⊢ γ]](η) a′ ⟺ a, a′ ∈ γ^{A} and a = a′

Figure 5.16: Interpretation of Refinement Types

logical factoring condition.

Remark 5.6.3 We make the obvious extension of Definition 4.3.18 (wellstructured expressions) to λ_{⊑}. As for the λ_{(:)}-calculus, it turns out that wellstructured expressions have a well-defined interpretation. In fact, some other expressions have an interpretation too, for example: (λn : nat.n)?_{(b:bool)⊥} is interpreted as the empty per.

For A a λ_{⊑}-Henkin interpretation we define Γ ⊨^{A} U wf when η [[Γ]] η′ implies [[Γ ⊢ U]] (η) = [[Γ ⊢ U]] (η′). That is, well-formedness is interpreted semantically as equal environments giving equal interpretations. For η ⊨^{A} Γ we define Γ ⊨^{A,η} r : ϕ to mean: [[Γ ⊢ r]](η) is a subper of [[Γ ⊢ ϕ]](η) and Γ ⊨^{A,η} P to mean: η ⊨ Γ ⇒ η ∈ [[Γ ⊢ P]]. In particular, then, Γ ⊨^{A,η} r ⊑_{ϕ} r′ when R ⊆ Φ and R′ ⊆ Φ; R; Φ, where R = [[Γ ⊢ r]]^{A}(η), R′ = [[Γ ⊢ r′]]^{A}(η), and Φ = [[Γ ⊢ ϕ]]^{A}(η). We will see below that this ‘asymmetric’ meaning of refinement has a more symmetric formulation.

For judgement Γ ⊢ J, we write Γ ⊨^{A} J when Γ ⊨^{A,η} J for all η ⊨^{A} Γ.

Definition 5.6.4 Let ⟨Sg, Ax⟩ be a λ_{⊑}-axiom system, and let A be a λ_{⊑}-Henkin interpretation of signature Sg. We say that A is a model of ⟨Sg, Ax⟩ when

• for each well-formed axiom Γ ⊢ P, for all η ∈ [[Γ]]^{A}, η ∈ [[Γ ⊢ P]]^{A}. We write this as Γ ⊨^{A} P.

b [[Γ, x : ϕ, Γ′ ⊢ x]]⟨η, a, η′⟩ b′ ⟺ b {a}_{[[Γ⊢ϕ]](η)} b′

{[[Γ ⊢ r_{1}]] = m_{1} ⋯ [[Γ ⊢ r_{n}]] = m_{n} \atop b [[Γ ⊢ k(r_{1}, …, r_{n})]](η) b′ ⟺ {∃a_{i} m_{i}(η) a′_{i} . b = k^{A}(a_{1}, …, a_{n}) and \atop b′ = k^{A}(a′_{1}, …, a′_{n})}}

b [[Γ ⊢ ∗]](η) b′ ⟺ b, b′ ∈ A^{1}

[[Γ ⊢ r]] = m [[Γ ⊢ r′]] = m′

b [[Γ ⊢ ⟨r, r′⟩]](η) b′ ⟺ Proj_{1}^{σ,τ}(b) m(η) Proj_{1}^{σ,τ}(b′) and Proj_{2}^{σ,τ}(b) m′(η) Proj_{2}^{σ,τ}(b′)

[[Γ, x : ϕ ⊢ r]] = m

f [[Γ ⊢ λx : ϕ.r]](η) f′ ⟺ ∀a [[Γ ⊢ ϕ]](η) a′ . App(f, a) m⟨η, a⟩ App(f′, a′)

{[[Γ ⊢ ϕ]] = R \atop [[Γ ⊢ ?_{ϕ}]](η) = R(η)}

[[Γ ⊢ r]] = m

[[Γ ⊢ π_{1}(r)]](η) = {(Proj_{1}^{σ,τ}(a), Proj_{1}^{σ,τ}(a′)) | a m(η) a′}

[[Γ ⊢ r]] = m

[[Γ ⊢ π_{2}(r)]](η) = {(Proj_{2}^{σ,τ}(a), Proj_{2}^{σ,τ}(a′)) | a m(η) a′}

[[Γ ⊢ r]] = m [[Γ ⊢ r′]] = m′

[[Γ ⊢ rr′]](η) = {( App(f, a), App(f′, a′) ) | f m(η) f′, a m′(η) a′}

[[Γ ⊢ r]] = m [[Γ, x : ϕ ⊢ r′]] = m′

b [[Γ ⊢ let x : ϕ be r in r′]](η) b′ ⟺ ∃a m(η) a′ . b m′⟨η, a⟩ b′ Figure 5.17: Interpretation of Refinement Terms

[[Γ ⊢ ⊥]] = ∅
[[Γ ⊢ P ⊃ P′]] = {η ⊨ Γ | η ∉ [[Γ ⊢ P]] or η ∈ [[Γ ⊢ P′]]}

[[Γ ⊢ ∀x : ϕ.P]] = {η ⊨ Γ | ∀a ∈ [[Γ ⊢ ϕ]](η) . ⟨η, a⟩ ∈ [[Γ, x : ϕ ⊢ P]]}

{[[Γ ⊢ r_{1}]] = R_{1} ⋯ [[Γ ⊢ r_{n}]] = R_{n} \atop [[Γ ⊢ F (r_{1}, …, r_{n})]] = \{η ⊨ Γ | ∀a_{i} ∈ R_{i}(η) . ⟨a_{1}, …, a_{n}⟩ ⊆ F^{A}\}}

[[Γ ⊢ r]] = R [[Γ ⊢ r′]] = R′ [[Γ ⊢ ϕ]] = Φ

[[Γ ⊢ r ⊑_{ϕ} r′]] = {η ⊨ Γ | R(η) ⊆ Φ(η) ∧ R′(η) ⊆ Φ(η); R(η); Φ(η)}

[[Γ ⊢ ϕ]] = Φ [[Γ ⊢ ϕ′]] = Φ′

[[Γ ⊢ ϕ ⊑ ϕ′]] = {η ⊨ Γ | Φ(η) ⊇ Φ′(η)}

Figure 5.18: Interpretation of Propositions

• for each well-formed axiom Γ ⊢ k : ϕ_{1}, …, ϕ_{n} → ψ, for all η ⊨^{A} Γ, if a_{i} [[Γ ⊢ ϕ_{i}]]^{A}(η) a′_{i} (i = 1, n), then k^{A}(a_{1}, …, a_{n}) [[Γ ⊢ ψ]]^{A}(η) k^{A}(a′_{1}, …, a′_{n}). We write this as Γ ⊨^{A} k : ϕ_{1}, …, ϕ_{n} → ψ

The meanings of the judgements may be equivalently expressed in terms of equivalence classes. We will use cl and cl′ as metavariables for equivalence classes, and by writing cl ∈ R, we mean that cl is a class of R, rather than a value.

Lemma 5.6.5

1. Γ ⊨^{η} r : ϕ when ∀cl ∈ [[Γ ⊢ r]](η) . ∀x ∈ cl . ∀x′ ∈ cl . x [[Γ ⊢ ϕ]](η) x′.

2. Γ ⊨^{η} r ⊑_{ϕ} r′ when Γ ⊨^{η} r : ϕ and ∀cl′ ∈ [[Γ ⊢ r′]](η) . ∃cl ∈ [[Γ ⊢ r]](η) . ∀x ∈ cl .∀x′ ∈ cl′ . x [[Γ ⊢ ϕ]](η) x′.

Proof: We prove part (2). Suppose Γ ⊨^{η} r ⊑_{ϕ} r′. The literal reading is that there is are inclusions of pers R′ ⊆ Φ; R; Φ and R ⊆ Φ, where R = [[Γ ⊢ r]](η), R′ = [[Γ ⊢ r′]](η) and Φ = [[Γ ⊢ ϕ]](η). Let cl′ ∈ R′. Then cl′ ∈ Φ; R; Φ. So for every x′ ∈ cl′ there exists x_{1}, x_{2} ∈ R such that x′ Φ x_{1} R x_{2} Φ x′. Let cl be the set of such x_{1}. Then we have cl Φ cl′.

Conversely, suppose ∀cl′ ∈ R′ . ∃cl ∈ R . cl Φ cl′. Let x′_{1} R′ x′_{2}. Then x′_{1}, x′_{2} ∈ cl′ for some cl′ ∈ R′, and so there exists a cl ∈ R such that cl Φ cl′. Choose any x ∈ cl. Then x′_{1} Φ x R x Φ x′_{2}.

Lemma 5.6.6 For all determined terms t, the per [[Γ ⊢ t]](η) is a singleton class.

Proof: Induction over preterms Γ ⊢ t.

We can formalise a sense in which λ_{⊑}-interpretations generalise the semantics of the two subcalculi. First observe that λ_{?}- and λ_{(:)}-axiom systems are also λ_{⊑}-axiom systems. Now λ_{?}- and λ_{(:)}-Henkin interpretations also give rise to λ_{⊑}- Henkin interpretations, and similarly for environments, though the interpretation functions are different. Terms from λ_{?} and λ_{(:)} are interpreted as sets, but terms in λ_{⊑} are interpreted as pers. Now the λ_{⊑}-interpretation of terms from the subcalculi is a special kind of per. In particular, types and terms from λ_{?} are interpreted as discrete pers. Terms from λ_{(:)} (i.e. determined terms) are interpreted as indiscrete pers. We will subscript interpretations with the calculus.

Define two mappings ι and κ from sets to pers. Let S be a set. Then we define pers ιS and κS as:

x ιS y ⟺ x, y ∈ S and x = y discrete per
x κS y ⟺ x, y ∈ S  indiscrete per

Proposition 5.6.7 Let r and τ be a well-formed term and type in λ_{?}. Then:

• [[Γ ⊢ r]]_{λ_{⊑}}^{A}(η) = ι ([[Γ ⊢ r : τ]]_{λ_{?}}^{A}(η))

• [[Γ ⊢ τ]]_{λ_{⊑}}^{A}(η) = ι (τ^{A})

Let t, ϕ and P be a pre-term, -refinement type and -proposition in λ_{(:)}. Then:

• [[Γ ⊢ t]]_{λ_{⊑}}^{A}(η) = κ ([[Γ ⊢ t]]_{λ_{(:)}}^{A}(η))

• [[Γ ⊢ ϕ]]_{λ_{⊑}}^{A}(η) = [[Γ ⊢ ϕ]]_{λ_{(:)}}^{A}(η)

• [[Γ ⊢ P]]_{λ_{⊑}}^{A}(η) = [[Γ ⊢ P]]_{λ_{(:)}}^{A}(η)

Proof: Induction over λ_{?}- and λ_{(:)}-expressions.

Proposition 5.6.8 The λ_{⊑}-calculus is a conservative extension of λ_{?} and λ_{(:)} in the following (semantic) sense:

• Let ⟨Sg, Ax⟩ be a λ_{?}-axiom system, and A a λ_{?}-Henkin model of ⟨Sg, Ax⟩. Let η ⊨^{A} Γ and suppose that r and r′ are terms of λ_{?}. Then,

Γ ⊨_{λ_{⊑}}^{A,η} r ⊑_{τ} r′ ⟺ Γ ⊨_{λ_{?}}^{A,η} r ⊑_{τ} r′

• Let ⟨Sg, Ax⟩ be a λ_{(:)}-axiom system, and A a λ_{(:)}-Henkin model of ⟨Sg, Ax⟩. Let η ⊨^{A} Γ and suppose that t and t′ are terms of λ_{(:)}. Then,

Γ ⊨_{λ_{⊑}}^{A,η} t =_{ϕ} t′ ⟺ Γ ⊨_{λ_{(:)}}^{A,η} t =_{ϕ} t′

Proof:

Γ ⊨_{λ_{⊑}}^{A,η} r ⊑_{τ} r′ ⟺ [[Γ ⊢ r′]]_{λ_{⊑}}^{A}(η) ⊆ [[Γ ⊢ τ]]_{λ_{⊑}}^{A}(η); [[Γ ⊢ r]]_{λ_{⊑}}^{A}(η); [[Γ ⊢ τ]]_{λ_{⊑}}^{A}(η)
⟺ ι ([[Γ ⊢ r′]]_{λ_{?}}^{A}(η)) ⊆ ι (τ^{A}); ι ([[Γ ⊢ r]]_{λ_{?}}^{A}(η)); ι (τ^{A})
⟺ [[Γ ⊢ r′ : τ]]_{λ_{?}}^{A}(η) ⊆ [[Γ ⊢ r : τ]]_{λ_{?}}^{A}(η)
⟺ Γ ⊨_{λ_{?}}^{A,η} r ⊑_{τ} r′
Γ ⊨_{λ_{⊑}}^{A,η} t =_{ϕ} t′ ⟺ ∀cl ∈ [[Γ ⊢ t]]_{λ_{⊑}}^{A}(η) . ∃cl′ ∈ [[Γ ⊢ t′]]_{λ_{⊑}}^{A}(η) . cl [[Γ ⊢ ϕ]]_{λ_{⊑}}^{A}(η) cl′ ∧
∀cl′ ∈ [[Γ ⊢ t′]]_{λ_{⊑}}^{A}(η) . ∃cl ∈ [[Γ ⊢ t]]_{λ_{⊑}}^{A}(η) . cl [[Γ ⊢ ϕ]]_{λ_{⊑}}^{A}(η) cl′
⟺ the class [[Γ ⊢ t]]_{λ_{⊑}}^{A}(η) [[Γ ⊢ ϕ]]_{λ_{⊑}}^{A}(η) the class [[Γ ⊢ t′]]_{λ_{⊑}}^{A}(η)
⟺ κ ([[Γ ⊢ t]]_{λ_{(:)}}^{A}(η)) [[Γ ⊢ ϕ]]_{λ_{(:)}}^{A}(η) κ ([[Γ ⊢ t′]]_{λ_{(:)}}^{A}(η))
⟺ ∀a ∈ [[Γ ⊢ t]]_{λ_{(:)}}^{A}(η).∀a′ ∈ [[Γ ⊢ t′]]_{λ_{(:)}}^{A}(η) . a [[Γ ⊢ ϕ]]_{λ_{(:)}}^{A}(η) a′

We will need the generalisations of some lemmas used in the soundness and completeness proofs of the two subcalculi.

Lemma 5.6.9 (Substitution Lemma) If Γ ⊨^{A,η} t_{i} : ϕ_{i} (i = 1, …, n), then

[[x_{1} : ϕ_{1}, …, x_{n} : ϕ_{n} ⊢ U]]^{A} ⟨a_{1}, …, a_{n}⟩ = [[Γ ⊢ U[t_{i}/x_{i}]]]^{A}(η)

where a_{i} ∈ [[Γ ⊢ t_{i}]]^{A}(η) (so ⟨a_{1}, …, a_{n}⟩ ⊨^{A} x_{1} : ϕ_{1}, …, x_{n} : ϕ_{n}).

Proof: Induction over x_{1} : ϕ_{1}, …, x_{n} : ϕ_{n} ⊢ U. Although this is written the same as the substitution lemma for λ_{(:)} (Lemma 4.5.4), there it is stated using sets, whereas here we use pers. The analogues of Lemmas 4.5.5 and 4.5.6 follow similarly.

Theorem 5.6.10 (Soundness) Let A be a Henkin Model of axiom system ⟨Sg, Ax⟩. If ⟨Sg, Ax⟩ ▷ Γ ⊢ B (where B ranges over basic judgements) then Γ ⊨^{A} B. In particular, if ⟨Sg, Ax⟩ ▷ Γ ⊢ ϕ wf then Γ ⊨^{A} ϕ wf, if ⟨Sg, Ax⟩ ▷ Γ ⊢ P wf then Γ ⊨^{A} P wf, if ⟨Sg, Ax⟩ ▷ Γ ⊢ r : ϕ then Γ ⊨^{A} r : ϕ, and if ⟨Sg, Ax⟩ ▷ Γ ⊢ P then Γ ⊨^{A} P.

Proof: Simultaneous induction over derivations of all judgements. We can simplify the proof of soundness of r ⊑_{ϕ} r′ by observing that, for those rules which are ⊑, that [[r]] ⊇ [[r′]] ⇒ ⊨ r ⊑ r′. Hence, we need only show that R′ ⊆ R, from which R′ ⊆ Φ; R; Φ follows. We work through some key cases (omitting the proofs that R ⊆ Φ).

• (Variables) Suppose ⊨ Γ, x : ϕ, Γ′ wf. Let ⟨η, a, η′⟩ ⊨ Γ, x : ϕ, Γ′ and suppose that b [[Γ, x : ϕ, Γ′]]⟨η, a, η′⟩ b′. Then, b {a}_{[[Γ⊢ϕ]](η)} b′, so b [[Γ ⊢ ϕ]](η) b′, and b [[Γ, x : ϕ, Γ′ ⊢ ϕ]]⟨η, a, η′⟩ b′.

• (Constants) Fix η ⊨ Γ. Let b [[Γ ⊢ k(r)]](η) b′, so b = k^{A}(a), b′ = k^{A}(a′) for some a [[Γ ⊢ r]](η) a′. By the soundness of Γ ⊢ r : ϕ we have a [[Γ ⊢ ϕ]](η) a′. Now Γ′ ⊆ Γ so suppose Γ ≡ Γ_{1}, Γ′, Γ_{2}, η ≡ ⟨η_{1}, η′, η_{2}⟩. Then [[Γ ⊢ ϕ]](η) = [[Γ′ ⊢ ϕ]](η′), so a [[Γ′ ⊢ ϕ]](η′) a′, and since A models the axiom, k^{A}(a) [[Γ′ ⊢ ψ]](η′) k^{A}(a′). Hence b [[Γ ⊢ ϕ]](η) b′.

• (Function Equations (β)) Suppose Γ, x : ϕ ⊨ t : ψ and Γ ⊨ t′ : ϕ. Then b [[Γ ⊢ (λx : ϕ.t)t′]](η) b′ iff there exists elements f, f′, a, a′ such that f [[Γ ⊢ λx : ϕ.t]](η) f′, a [[Γ ⊢ t′]](η) a′ and b = App(f, a), b′ = App(f′, a′). This holds iff b [[Γ, x : ϕ ⊢ t]](⟨η, a⟩) b′ for a ∈ [[Γ ⊢ t]](η), iff b [[Γ ⊢ t[t′/x]]](η) b′ (substitution lemma).

• (Let Beta) Suppose η ⊨ Γ and b [[Γ ⊢ let x : ϕ be t in r]](η) b′. This is the same as b [[Γ, x : ϕ ⊢ r]]⟨η, a⟩ b′ for some a ∈ [[Γ ⊢ t′]](η). By the Substitution Lemma, this is the same as b [[Γ ⊢ r[t/x]]](η) b′.

• Let Term Equalities:

– (Projections) Suppose b [[Γ ⊢ let x : ϕ_{1} × ϕ_{2} be r in π_{1}(x)]](η) b′. This is when there exists a ∈ [[Γ ⊢ r]](η) such that b [[Γ, x : ϕ_{1} × ϕ_{n} ⊢ π_{1}(x)]]⟨η, a⟩ b′. Hence b [[Γ ⊢ π_{1}(r)]](η) b′.

– (Abstractions) This is similar to the proof in Chapter 3, but uses the stronger factoring condition. For the sake of simplicity, we will consider closed terms. We must show that

[[let z : Π_{x:ϕ}ψ in λx : ϕ.t[zx/y]]] = [[λx : ϕ.(let y : ψ in t)]]

Now f ∈ [[let z : Π_{x:ϕ}ψ in λx : ϕ.t[zx/y]]] when

∃a ∈ [[Π_{x:ϕ}ψ]] . ∀b ∈ [[ϕ]] . fb ∈ [[z : Π_{x:ϕ}ψ, x : ϕ ⊢ t[zx/y]]]⟨a, b⟩ (5.1)

and f ∈ [[λx : ϕ.(let y : ψ in t)]] when

∀b ∈ [[ϕ]] . ∃a_{b} ∈ [[x : ϕ ⊢ ψ]](b) . fb ∈ [[x : ϕ, y : ψ ⊢ t]]⟨b, a_{b}⟩ (5.2)

We follow the same line of reasoning as in Chapter 3, p. 80, to prove these two statements equivalent. The interesting direction is showing that (5.2) implies (5.1). Define h : σ^{A} → (σ×τ)^{A} as (b ∈ σ^{A} ↦ ⟨b, a_{b}⟩) and f′ as [[λp : Σ_{x:ϕ}ψ ⊢ t[π_{1}p/x, π_{2}p/y]]]. Then, since (5.2) says that f̄ = h; f̄′, by the logical factoring condition, there exists g ∈ [[ϕ → Σ_{x:ϕ}ψ]] such that ḡ; f̄′ = f̄. The remainder of the proof follows Chapter 3.

• (Logical Congruence) Suppose Γ ⊨ r ⊑_{ϕ} r′ and Γ ⊨ r : (x : ϕ)P. Let η ⊨ Γ, and suppose b R′(η) b′. Then there exists b_{1}, b_{2} such that b Φ(η) b_{1} R(η) b_{2} Φ(η) b′. Now b_{1} R(η) b_{2} implies that b_{1} [[Γ ⊢ (x : ϕ)P]](η) b_{2}, so Γ, x : ϕ ⊨^{η,b_{1}} P, and since b_{2} [[Γ ⊢ ϕ]](η) b′, we have Γ, x : ϕ ⊨_{⟨η,b′⟩} P, and so b_{2} [[Γ ⊢ (x : ϕ)P]](η) b′. Similarly, b [[Γ ⊢ (x : ϕ)P]](η) b_{1}, and so b [[Γ ⊢ (x : ϕ)P]](η) R(η) [[Γ ⊢ (x : ϕ)P]](η) b′.

• Refinement Rules: The soundness of r ⊑_{ϕ} r′ (Figures 5.7, 5.8, 5.9 and 5.10) follows the corresponding proofs in Chapter 3.

Since η [[Γ]] η′ implies [[Γ ⊢ r]](η) = [[Γ ⊢ r]](η′) we can give the semantics as a mapping from [[Γ]] classes. Sometimes we write an environment as [[t]], meaning any member of the class of [[t]].

In Chapter 4 we had the problem of formulating a completeness result, since the interpretation of refinement types as pers did not correspond exactly to the rules of the calculus as they currently stand.

There were two kinds of mismatch. On the one hand, a term could be ‘wellformed’ in the semantics, by virtue of having a unique interpretation, yet not be syntactically well-formed, an example being (λn : even.∗)3. The other problem arose with higher-order terms, and was due to the calculus requiring arguments to an abstraction to have the refinement type on the abstraction, but the model just needing equality of arguments at that refinement type to give equal results. For example, λf : nat → nat . 3 has refinement type (even → nat) → nat in the model but not in the calculus.

We got round this in Chapter 4 by defining a contextual equivalence ≃_{ϕ} on terms such that if ⊨ t : ϕ then there was a t′ such that t′ ≃_{ϕ} t and t′ : ϕ. For the above two examples, we have (λn : even.∗)3 ≃_{1} ∗ and λf : nat → nat . 3 ≃_{even→nat}

λf : even → nat . 3.

Another possibility is to restrict the statement of completeness to avoid these classes of terms, and this is what we do in this chapter. This has the virtue of being simpler, and it also makes it easier to extend the completeness theorem using suggestions in Chapter 6.

Suppose some judgement is true in all λ_{⊑}-models of the relevant axiom system. In order to show that the judgement is provable we first assume that it is wellformed, where well-formedness of the judgement Γ ⊢ r : ϕ means that Γ ⊢ ϕ wf and Γ ⊢ r wf, that is, Γ ⊢ r : ϕ′ for some ϕ′. We then make the additional assumption that the judgement is of rank less than or equal to 1, where the rank is defined recursively for each syntactic category, the idea being to exclude any higher-order refinement types. For example, Rank (nat × bool → nat) = 1,

Rank ((even → nat) → nat) = 2.

The completeness proof has the same pattern as in previous chapters. We construct a term model from an appropriate notion of Henkin theory. As with Definition 4.5.8 in the λ_{(:)}-calculus, we regard theories as infinite contexts, rooted on the left.

Definition 5.6.11 Let ⟨Sg, Ax⟩ be a λ_{⊑}-axiom system. A λ_{⊑}-Henkin theory over ⟨Sg, Ax⟩ is a well-formed infinite context, Γ, closed under derivation from ⟨Sg, Ax⟩ such that:

• if ∃x : ϕ.P ∈ Γ then for some term Γ ⊢ t : ϕ, P [t/x] ∈ Γ

• if (let x : ϕ in t ⊑_{χ} let y : ψ in t′) ∈ Γ, then there is a determined term Γ ⊢ f : ψ → ϕ, such that (∀y : ψ . t[fy/x] =_{χ} t′) ∈ Γ

Theorem 5.6.12 (Completeness) Let ⟨Sg, Ax⟩ be a λ_{⊑}-axiom system. For ⟨Sg, Ax⟩ ▷ Γ ⊢ B wf and Rank (Γ ⊢ B) ≤ 1, if Γ ⊨^{A} B for all λ_{⊑}-Henkin Models, A, of ⟨Sg, Ax⟩, then ⟨Sg, Ax⟩ ▷ Γ ⊢ B. In particular, assuming rank < 1, for ⟨Sg, Ax⟩ ▷ Γ ⊢ P wf, if Γ ⊨^{A} P for all models, A, of ⟨Sg, Ax⟩ then ⟨Sg, Ax⟩ ▷ Γ ⊢ P, and for ⟨Sg, Ax⟩ ▷ Γ ⊢ ϕ wf and ⟨Sg, Ax⟩ ▷ Γ ⊢ r wf, if Γ ⊨^{A} r : ϕ for all models, A, of ⟨Sg, Ax⟩ then ⟨Sg, Ax⟩ ▷ Γ ⊢ r : ϕ.

Proof: Let Γ be a consistent context. We sketch the construction of a model A and environment η ⊨^{A} Γ below, and use this to derive completeness.

1. Construct a maximal consistent λ_{⊑}-Henkin theory Γ_{∞} such that {P | Γ ⊢ P } ⊆ Γ_{∞}. The construction follows that of Theorem 4.5.16.

2. Construct the term model from open terms. Define τ^{A} as the set of equivalence classes of well-structured open terms of λ^{×→}, {u | Γ_{∞}, ⊥ ⊢ u : ϕ ∧ Γ_{∞} ⊢ ϕ : Ref (τ)} with respect to the same equivalence as for λ_{(:)}.

We construct a λ_{⊑}-Henkin interpretation A, by interpreting constant and predicate symbols syntactically. Prove that A satisfies the factoring condition, and so is a well-defined interpretation.

3. For η′ ⊨^{A} Γ′ and Γ_{∞} ⊢ B[η′/Γ′] wf, prove that Γ′ ⊨^{A,η′} B ⟺ Γ_{∞} ⊢ B[η′/Γ′]. This uses the characterisation of expressions in the term model given in Lemma 5.6.15 below.

4. A is a model of the axioms, by reasoning similar to the step on p. 130

5. For x_{1} : ϕ_{1}, …, x_{n} : ϕ_{n} the variables in Γ, we define the Γ-environment, η, as ⟨[x_{1}], …, [x_{n}]⟩. We can show that η ⊨^{A} Γ. Thus, we have shown that an arbitrary consistent context is satisfiable.

6. The final step is to show that if Γ ⊨^{A} B then Γ ⊢ B. This is just as for λ_{(:)}. Suppose Γ ⊬ P. Then Γ, ¬P is consistent, so by the previous steps, there is an environment, η, such that η ⊨^{A} Γ, ¬P, so Γ ⊭^{A,η} P, and Γ ⊭ P. The situation for refinement typings can be reduced to that of propositions, since Γ ⊨^{A,η} r : ϕ is equivalent to Γ ⊨^{A,η} r ⊑_{ϕ} r. The crucial point is that the permissive well-formedness rule for refinements (Refinement) means that r ⊑_{ϕ} r is well-formed even though r need not have refinement type ϕ. Thus, Γ ⊨ r ⊑_{ϕ} r implies Γ ⊢ r ⊑_{ϕ} r so, by Refinement Elimination, Γ ⊢ r : ϕ.

In order to prove step 3, we use some lemmas. First we characterise the interpretation of refinement terms and types in the

term model, A. In the following, the semantic interpretation [[ ]] is to be understood as being in A and Γ_{∞} is fixed. As discussed above, we make restrictions so that the pers correspond to equality.

For the completeness of λ_{(:)}, we used an implicit definition of sat. Here we will use an explicit definition.

Definition 5.6.13 For Γ_{∞} ⊢ r, t wf, define t sat r to mean Γ_{∞} ⊢ r ⊑′ t.

Refinement types are interpreted as pers whose equivalence classes are in oneto-one correspondence with determined terms.

Lemma 5.6.14

[u] [[Γ ⊢ ϕ]](η) [u′] ⟺ [u] [[Γ ⊢ t]](η) [u′], for some Γ ⊨^{η} t : ϕ Proof: Define a term Γ ⊢ t_{ϕ} by induction on ϕ for each Γ ⊢ ϕ. Let t_{1} = ∗, t_{γ} = u, t_{Σ_{x:ϕ}ψ} = ⟨t_{ϕ}, t_{ψ}[t_{ϕ}/x]⟩, t_{Π_{x:ϕ}ψ} = λx : ϕ.t_{ψ}, and t_{(x:ϕ)P} = t_{ϕ}.

We prove, by induction over ϕ, that [u] [[Γ ⊢ ϕ]](η) [u′] iff [u] [[Γ ⊢ t_{ϕ}]](η) [u′] and Γ ⊨^{A,η} t_{ϕ} : ϕ. The interesting cases are γ and (x : ϕ)P. Clearly, we have [u] [[Γ ⊢ γ]](η) [u′] iff [u] [[Γ ⊢ u]](η) [u′]. For the (x : ϕ)P case, if [u] [[Γ ⊢ (x : ϕ)P]](η) [u′] then [u] ∈ [[Γ, x : ϕ ⊢ P]](η), so we deduce that Γ ⊨^{A,η} t_{ϕ} : (x : ϕ)P.

Lemma 5.6.15 With the rank restriction: Let η ⊨^{A} Γ.

1. [u] [[Γ ⊢ ϕ]](η) [u′] ⟺ Γ_{∞} ⊢ u =_{ϕ[η/Γ]} u′

2. For Γ ⊢ r wf, [u] [[Γ ⊢ r]](η) [u′] ⟺ [u] [[t]] [u′] for some t sat r[η/Γ]

3. For Γ_{∞} ⊢ P [η/Γ] wf, Γ ⊨^{A,η} P ⟺ Γ_{∞} ⊢ P [η/Γ]

Proof: Simultaneous induction over expressions. The inductive ordering is

P, P′ < P ⊃ P′
ϕ, P [t] < ∀x : ϕ.P
ϕ < r ⊑_{ϕ} r′
r < F (r)
ϕ, ϕ′ < ϕ ⊑ ϕ′
ϕ, ψ[t] < Σ_{x:ϕ}ψ
ϕ, ψ[t] < Π_{x:ϕ}ψ
ϕ, P [t] < (x : ϕ)P

The proof for let x : ϕ be r in r′ uses Lemma 5.5.12 (1). We prove the cases for propositions, writing \overline{U} for U[η/Γ].

• The ⊥, P ⊃ P′, ∀x : ϕ.P and ϕ ⊑ ϕ′ cases are proven as in Chapter 4.

• Γ ⊨^{A,η} F (r) means (using the inductive hypothesis on r): for all t, t sat \overline{r} implies Γ_{∞} ⊢ F (t). Since Γ_{∞} ⊢ F (\overline{r}) wf, we must have Γ_{∞} ⊢ \overline{r} : ϕ for some ϕ. We assume, without loss of generality, that F : Pred (τ) and r has the canonical form let x : ψ in t′. We must show that Γ ⊨^{A,η} F (r) is equivalent to Γ_{∞} ⊢ F (\overline{r}). Suppose Γ ⊨^{A,η} F (r). If ∃x : \overline{ψ}.⊤ ∈ Γ_{∞} (\overline{ψ} is inhabited) then t′[x] sat \overline{r}, so Γ_{∞}, x : \overline{ψ} ⊢ F (t′). If ∃x : \overline{ψ}.⊤ ∉ Γ_{∞} then by maximality, ∀x : \overline{ψ}.⊥ ∈ Γ_{∞} and by consistency, we also infer that Γ_{∞}, x : \overline{ψ} ⊢ F (t′). Then, by Refinement Type Introduction, Γ_{∞}, x : \overline{ψ} ⊢ t′ : (y : ϕ)F (y), so by Let Terms Γ_{∞} ⊢ let x : \overline{ψ} in \overline{t′} : (y : ϕ)F (y), and using subject refinement Γ_{∞} ⊢ \overline{r} : (y : ϕ)F (y). Hence, using Predicates, Γ_{∞} ⊢ F (\overline{r}). Conversely, if Γ_{∞} ⊢ F (\overline{r}) then, by Refinement Type Introduction, Γ_{∞} ⊢ \overline{r} : (y : ϕ)F (y), so by the definition of sat and using subject refinement, if t sat \overline{r} then Γ_{∞} ⊢ t : (y : ϕ)F (y), and so Γ_{∞} ⊢ F (t).

• Γ ⊨^{A,η} r ⊑_{ϕ} r′: We show that this is equivalent to \overline{r} ≲\overline{ϕ} \overline{r′} which, in turn, is equivalent to Γ_{∞} ⊢ \overline{r} ⊑\overline{ϕ} \overline{r′}. Suppose Γ ⊨^{A,η} r ⊑_{ϕ} r′. By Lemma 5.6.5, for all t′ sat\overline{r′} there exists t sat\overline{r} such that t [[Γ ⊢ ϕ]](η) t′ (where t and t′ are representatives of equivalence classes). By the inductive hypotheses, and reasoning as in Chapter 4, p. 133, we have Γ_{∞} ⊢ t =\overline{ϕ} t′. Now suppose Γ_{∞} ⊢ \overline{r′} ⊑\overline{ϕ} t′′. By Corollary 5.5.6, there exists a (determined) term t′ such that Γ_{∞} ⊢ t =\overline{ϕ} t′′ and t′ sat \overline{r′}. Then there exists a t such that t sat \overline{r} and Γ_{∞} ⊢ t =\overline{ϕ} t′. By the definition of ⊑, we have Γ_{∞} ⊢ \overline{r} ⊑\overline{ϕ} t′′, and so Γ_{∞} ⊢ \overline{r} ⊑\overline{ϕ} t′′ =\overline{ϕ} t =\overline{ϕ} t′, and hence Γ_{∞} ⊢ \overline{r} ⊑\overline{ϕ} t′. Thus, \overline{r} ≲\overline{ϕ} \overline{r′}. Conversely, suppose that \overline{r} ≲\overline{ϕ} \overline{r′} and t′ sat \overline{r′}. Then Γ_{∞} ⊢ \overline{r′} ⊑\overline{ϕ} t′ so Γ_{∞} ⊢ \overline{r} ⊑\overline{ϕ} t′. By Corollary 5.5.6, there exists a t such that t sat \overline{r} and t =_{ϕ} t′, so by Lemma 5.6.5, Γ ⊨^{A,η} r ⊑_{ϕ} r′. Now, we show that \overline{r} ≲\overline{ϕ} \overline{r′} ⟺ Γ_{∞} ⊢ \overline{r} ⊑\overline{ϕ} \overline{r′}. The reasoning is the same as the corresponding step in the completeness proof for the λ_{?}-calculus. We can assume, without loss of generality, that the terms are in canonical form, and since \overline{r}, \overline{r′} : \overline{ϕ} by the assumption of well-formedness, Lemma 5.5.7 gives canonical forms which are equal to the terms at \overline{ϕ}. Hence we have

let x : ψ_{1} in t_{1} ≲Γ_{∞} ; \overline{ϕ} let y : ψ_{2} in t_{2}

If ψ_{2} ∈ Γ_{∞} then, by Lemma 5.5.15, there exists a term Γ_{∞}, y : ψ_{2} ⊢ t : ψ_{1} such that Γ_{∞}, y : ψ_{2} ⊢ t_{1}[t] =_{ϕ} t_{2}. Now suppose ψ_{2} is not inhabited. Let t be any term in the type below \overline{ϕ}. Such a term exists because of the assumption that all types are inhabited. Then we have, Γ_{∞}, y : ψ_{2} ⊢ ⊥, so Lemma 4.3.17 implies that Γ_{∞}, y : ψ_{2} ⊢ t : ϕ. Similarly, Γ_{∞}, y : ψ_{2} ⊢ t_{1}[t/x] =_{χ} t_{2} wf, and Γ_{∞}, y : ψ_{2} ⊢ t_{1}[t/x] =_{χ} t_{2}.

Then, in the context Γ_{∞}, the term let x : ψ_{1} in t_{1} refines (at ϕ) to let y : ψ_{2} in (let x : ψ_{1} in t_{1}), which refines to let y : ψ_{2} in t_{1}[t[y]], and this equals let y : ψ_{2} in t_{2}[y]. Hence Γ_{∞} ⊢ \overline{r} ⊑\overline{ϕ} \overline{r′}.

The characterisation of the interpretation of expressions in Lemma 5.6.15 is specific to the term model. For example, in the term model, [[?_{nat→nat}]] consists of equivalence classes of terms of type nat → nat, but in the full set-theoretic function hierarchy (Definition 3.4.3), the classes consist of arbitrary functions.

In Lemma 5.6.6, we showed that determined terms are interpreted as a single equivalence class. We can give a simple characterisation of that class in the term model.

Lemma 5.6.16 With the rank restriction:

[u_{1}] [[Γ ⊢ t]](η) [u_{2}] ⟺ u_{1} sat^{Γ_{∞}} t[η/Γ] and u_{2} sat^{Γ_{∞}} t[η/Γ]

Proof: Follows from Lemmas 5.6.14 and 5.6.15. By Lemma 5.6.15, we see that in the term model, arbitrary refinement terms are interpreted as pers consisting of classes of this form.

Corollary 5.6.17 With the rank restriction, if Γ ⊢ r : ϕ, then: Γ ⊢ r : (x : ϕ)P iff for all determined t, if Γ ⊢ r ⊑_{ϕ} t then Γ ⊢ P [t/x]

Proof: Both statements have the same interpretation, so the result follows from soundness and completeness.

Remark 5.6.18 The r : ϕ step in the proof of completeness suggests a general strategy for proving that r : (x : ϕ)P. First express r in canonical form as let y : ψ in t. Then prove that ∀y : ψ . P [t/x]. We know that if the refinement typing is true then, by the completeness of the propositional fragment, this proposition is provable. Hence, we conclude that let y : ψ in t : (x : ϕ)P and so, by subject refinement, r : (x : ϕ)P.

5.7 Conclusion

In this chapter we presented the λ_{⊑}-calculus, a refinement calculus based on the notions of refinement term and refinement type. In the next chapter we discuss how this could give a basis for a more comprehensive theory of software development.

Conclusions and Further Work

6.1 Conclusions

In this thesis we constructed a canonical refinement calculus based on the lambda calculus and classical first-order predicate logic, and studied its proof theory and semantics. Let us summarise the main points of this formalisation:

programs  — lambda terms
abstract programs — refinement terms
specifications  — refinement types

Formalisation of Refinement

We gave a set-theoretic semantics based on Henkin models for which the calculus was proven sound and complete. As far as we know, this is the first proof of completeness of any refinement calculus.

A key feature of this approach was the construction of the refinement calculus in a modular fashion, as the combination of two orthogonal extensions to the underlying programming language (in this case, the simply-typed lambda calculus). These subcalculi are interesting in their own right as they provide separate analyses of structured specifications and non-logical decomposition. ‘Full’ refinement, then, can be factored into logical equational reasoning and simple decomposition. We used a two-level formalisation of specifications, consisting of an underlying level of program types, and a more expressive level of program properties.

We now discuss how the issues raised in the introductory chapter have been addressed.

We set ourselves the task of investigating the logic and semantics of refinement calculi, and saw that it is possible to induce a refinement calculus from an extensional program logic and the equational theory of a programming language (in the canonical case of the simply-typed lambda calculus). We construct structured specifications — refinement types — and have a notion of equality at a specification. The syntactic category of refinement terms consists of combinations of specifications and programs.

First-order logic and the simply-typed lambda calculus can be modelled using Henkin interpretations, and given models of particular lambda theories and logics, we can form a model of the corresponding refinement calculus.

The refinement calculus is completely characterised by the underlying theories in the sense that it is complete (given certain restrictions) with respect to the class of models induced from the models of the underlying theories. Moreover, we can use the completeness theorems to deduce (under these restrictions) the conservativity of refinement calculi over program logics and (equational theories of) programming languages.

The refinement calculus can be thought of as being constructed from two subcalculi — a calculus of refinement terms and a calculus of refinement types. These calculi are useful in their own right. For example, we saw that terms of the λ_{?}-calculus can be evaluated in a program-like fashion. A factorisation theorem justifies us in regarding the subcalculi as being orthogonal extensions to the programming language and program logic.

The factorisation suggests an interesting possibility for the construction of a modular refinement tool, in which checking program correctness is a combination of type checking and theorem proving. The modularity would come from constructing a verifier, or ‘specification checker’, from an existing theorem prover (which we can think of as an oracle) and a type checker, for a program logic and programming language respectively. Then we would write a separate program to handle simple refinement and combine the two to get the refinement tool.

Although this thesis has presented a simple calculus, we believe that we have motivated the general methodology of inducing a refinement calculus from a programming language together with some logic, rather than constructing a development methodology from scratch. We believe that, from a theoretical standpoint, this approach is more likely to be useful for formal methods. It seems rather naive to expect programmers to treat “programming as a mathematical activity”, working directly in some refinement calculus. Indeed, this is a dangerous viewpoint insofar as it leads to taking the mathematical formalisation as the primary object of interest, thus distancing theory from actual programming practice. While formalisation of the relevant concepts is indeed desirable, it is more realistic to provide a theoretical underpinning for tool support. The intention here is not that the calculus should actually be used directly, but that it serves as an underlying theory.

We believe that factoring a complicated calculus into two subcalculi has proven its worth as a research methodology. Many of the extensions suggested below could also be first studied as extensions to each of the two subcalculi.

We believe that the principles outlined here are general enough to be applied to structures other than those traditionally studied — data flow diagrams for example. Since the logic is arbitrary (up to a point) we are not constrained by the type theory. It would be an interesting line of research to see how type-theoretic and semantic ideas could help there. The calculus could provide a foundation for other specification based formalisms, and we will make some specific suggestions below.

6.1.1 Refinement Terms

We would be interested to see how this calculus might be usefully combined with work on logical frameworks [Pfe96]. The use of logical variables there is an example of underdeterminism.

Although Lego has a richer type system than those studied here, a fragment of it could be studied using λ_{?} as a metalanguage. It would be interesting to use λ_{?} as a metalanguage for giving a semantics to Lego and to prove some metatheoretic results.

That the concept of underdeterminism arises both in computing science and in linguistics strengthens our belief that it is an important concept in the study of general informatics.

We could annotate the types with simple properties, such as whether or not an exception is raised at some point. This would be a useful intermediate stage between the calculus of refinement terms and the full refinement calculus.

6.1.2 Refinement Types

A number of systems have intersection and union types. This can not always be expressed in our system. For example, no refinement type corresponds to even → even ∧ odd → odd. Hayashi’s [Hay94b] intersection and union are even more powerful. It is not clear whether the degree of expressivity in this system is particularly useful (but see the comments about quotients below).

The two-level nature of the calculus suggests the construction of a modular tool in which checking program correctness is a combination of type checking and theorem proving. The modularity would come from constructing a ‘specification checker’ from an existing theorem prover and a type checker, for the program logic and programming language respectively. Indeed, this is similar to what is done in the interactive proof development systems, Nuprl and PVS, where type-checking can generate proof obligations.

6.2 Technical Extensions and Conjectures

We make some suggestions for various technical extensions and results for the calculi. The first two extensions, in particular, are aimed at tidying up the two main loose ends in this work — the restrictions on completeness in Chapter 4 and 5, and the restrictions on dependent refinement types in Chapter 5.

The final ideas (11, 12 and 13) are suggestions for reformulations of the calculi.

1. We had to place restrictions on the statements of completeness for λ_{(:)} and λ_{⊑} due to the underlying mismatch between the refinement type ϕ → ψ and its per semantics. For example, we could not prove that λf : nat → nat . f2 has refinement type (even → nat) → nat. The problem is that as the rules stand, for the abstraction λx : ϕ′.t to have refinement type ϕ → ψ we require ϕ′ ⊑ ϕ but, in this case, the refinement goes in the opposite direction, nat → nat ⊑ even → nat. It is not sound, in general, to say that λx : ϕ′.t : ϕ → ψ when ϕ ⊑ ϕ′ (and the other conditions). However, the following rule does appear to be sound. Define dom ϕ to be the set of terms with refinement type ϕ. Then,

\begin{matrix}Γ, x : ϕ′ ⊢ t : ψ & Γ ⊢ ϕ′ ⊑ ϕ \\ Γ ⊢ λx : ϕ.t : ϕ′ → ψ \end{matrix} (dom ϕ′ ⊆ dom ϕ)

The combination of ϕ′ ⊑ ϕ and dom ϕ′ ⊆ dom ϕ (i.e. x ϕ′ x ⇒ x ϕ x) means that dom ϕ′ = dom ϕ, and ϕ′ is a quotient of ϕ. The rule would let us prove

            λf : nat → nat . f2 : (even → nat) → nat

and may be enough to strengthen Theorems 4.5.16 and 5.6.12 (completeness of λ_{(:)} and λ_{⊑}) to unrestricted versions. Similarly, this rule is sound, and may be admissible

ϕ′ a quotient of ϕ ?_{ϕ} ⊑_{ϕ′} ϕ′ A provable consequence of the rule is ?_{nat→nat} ⊑_{even→nat} ?_{even→nat}. The combination of quotienting and the subset type-like refinement types we use might be interesting for specification. Many specification formalisms use some form of quotienting and it would be interesting to see it arise naturally here in order to get completeness.

2. In Chapter 5, we used nondependent refinement types to avoid the combination of underdetermined terms with refinement types. The same problem arose in Chapter 3 at the level of terms and led us to introduce let-terms. We could introduce a notion of let-types, therefore, where let x : ϕ be r in ψ has the obvious meaning. The rule of introduction would be

Γ ⊢ r : Π_{x:ϕ}ψ Γ ⊢ r′ : ϕ
Γ ⊢ rr′ : let x : ϕ be r′ in ψ

We would have a special rule for stubs.

Γ ⊢ r : ϕ Γ, x : ϕ ⊢ ψ wf
Γ ⊢ let x : ϕ be r in ?_{ψ} = ?_{let x:ϕ be r in ψ}

The refinement rules for let-types would be analogous to the equalities for let-terms. To give these, it is convenient to extend the singleton type notation of Remark 4.3.9 to arbitrary refinement terms: we write {r}_{ϕ} for the refinement type (x : ϕ)r ⊑_{ϕ} x. For example:

let x : ϕ be t in ψ = ψ[t/x]
let x : ϕ be r in {x}_{ϕ} = {r}_{ϕ}
let z : Σ_{x:ϕ}ψ be r in {π_{1}z}_{ϕ} = {π_{1}r}_{ϕ}

3. It may be that any maximal first-order λ^{×→}-Henkin theory is a first-order λ_{?}-Henkin theory. That is, the witness condition for refinements may follow from the condition for existentials.

4. We conjecture that the λ_{?} factoring condition is equivalent to the satisfaction of a choice axiom (or skolemisation):

⊨ ∀x : τ.∃y : σ.P ⊃ ∃f : τ → σ.∀x : τ.P [fx/y]

for all P.

5. We conjecture that the λ_{(:)}-calculus is a conservative extension of Aspinall’s λ_{≤{}} calculus [Asp95].

6. If we define a notion of ‘strong’ well-formedness for λ_{(:)} and λ_{⊑}, which requires the appropriate refinement typings for equalities and predications (i.e. t =_{ϕ} t′ when t, t′ : ϕ), then it should be that for a strongly well-formed P, if Γ ⊨ P then Γ ⊢ P. We should formulate the connection between the different restrictions for completeness of λ_{(:)} and λ_{⊑}.

7. In Remark 4.3.6 we suggested that axioms in λ_{(:)} (and λ_{⊑}) could be given in a particular form and this should be investigated further.

8. We conjecture that the Let Term Equality Abstractions in Chapter 5 is equivalent to the typed form, and so we could avoid the meta-judgement in the formal system.

9. A satisfactory account of the subrelations in Section 5.5 remains to be given. For example, does Eta commute with the other rules in ⊑^{s}?

10. We have taken refinements (of both kinds) to be atomic propositions rather than separate judgement classes. We conjecture that restricted calculi in which the atomic propositions are equalities, and the refinements are separate judgement classes would also be complete. If so, the full systems would be conservative extensions. This could be considered a more natural approach, as specification using refinement itself is more complex than just using the underlying program logic.

11. We conjecture that λ_{?} and λ_{⊑} are complete for the alternative semantics of Remark 3.4.12. This would let us avoid using the factoring conditions.

12. We could use the suggestive notation ⟨r, (x : ϕ)r′⟩ for let x : ϕ be r in ⟨x, r′⟩. A dependent form of the refinement rule Pairs would be:

?_{Σ_{x:ϕ}ψ} ⊑ ⟨?_{ϕ}, (x : ϕ)?_{ψ}⟩

13. There is some overlap between the refinement rules for terms and refinement types. We could combine the two judgements into the form r : ϕ ⊑ r′ : ϕ′, meaning r ⊑_{ϕ} r′ and ϕ ⊑ ϕ′. Some natural rules would be:

{Γ ⊢ ϕ ⊑ 1 \atop Γ ⊢ ?_{ϕ} : ϕ ⊑ ∗ : 1}

{Γ ⊢ χ ⊑ Π_{ϕ}ψ \atop Γ ⊢ ?_{χ} : χ ⊑ λy : ϕ.?_{ψ} : Π_{x:ϕ}ψ}

Using the notation introduced above we have:

{Γ ⊢ χ ⊑ Σ_{x:ϕ}ψ \atop Γ ⊢ ?_{χ} : χ ⊑ ⟨?_{ϕ}, (x : ϕ)?_{ψ}⟩ : Σ_{x:ϕ}ψ}

The overlap is clear when giving the rules for let-types. We could have, for example:

Γ, x : ϕ ⊢ r : ψ Γ ⊢ t : ϕ
Γ ⊢ let x : ϕ be t in r : let x : ϕ be t in ψ = r[t/x] : ψ[t/x]

14. We could base the refinement calculus on primitive definitions of ⊑ and =_{ϕ} rather than ⊑_{ϕ}. This might be more natural, as we usually omit the subscripted ϕ anyway.

6.3 Operational Semantics

As for the denotational semantics, we can give a modular operational semantics, by first giving a semantics to the subcalculi. Here we will just outline how to do this for the subcalculi. We restrict ourselves to the specific axiom systems of booleans and naturals.

6.3.1 Refinement Terms

Because terms of the calculus are a mixture of specification and program, we do not inherit a notion of reduction from the lambda-calculus, but we can give an operational semantics based on satisfaction of terms and properties by canonical terms.

The canonical terms are the closed terms of the form:

c ::= b | n | ∗ | ⟨c, c′⟩ | λx : σ.t

where t is an arbitrary determined term, b is one of the booleans true and false, and n is a numeral. The operational semantics is given in Figures 6.1 to 6.4, and consists of an evaluation relation on determined terms t ⇓ c, together with three ∗ ⇓ ∗ n ⇓ n b ⇓ b

\begin{matrix}t ⇓ c & t′ ⇓ c′ \\ ⟨t, t′⟩ ⇓ ⟨c, c′⟩ \end{matrix}

λx : σ.t ⇓ λx : σ.t

{t ⇓ ⟨c, c′⟩ \atop π_{1}(t) ⇓ c}  {t ⇓ ⟨c, c′⟩ \atop π_{2}(t) ⇓ c′}

\begin{matrix}t ⇓ λx : σ.t′′ & t′′[t′/x] ⇓ c \\ tt′ ⇓ c \end{matrix}

{t′[t/x] ⇓ c \atop let x : σ be t in t′ ⇓ c}

Figure 6.1: Evaluation

mutually recursive relations: an extensional equality on canonical terms c =_{σ} c′; a satisfaction relation between canonical and underdetermined terms, c ⊨ r; and the validity of propositions ⊨ P.

Since we want equality and refinement to be extensional for determined terms, but not for arbitrary underdetermined terms we first define a typed extensional equality on canonical terms, =_{σ}, by induction on σ.

The second component of the operational semantics is a satisfaction relation, c ⊨ r. For example, λx : σ.t ⊨ λx : σ.r when for all c : σ, for all d : σ, d ⊨ t[c] ⇒ d ⊨ r[c]. In fact, since there is a unique canonical form equal to t[c], we could have written t[c] ⊨ r[c], but we do not assume the uniqueness here.

Now we can define ⊨ r : σ as: for all c ⊨ r, c : σ. Next, for ⊨ r : σ, ⊨ r′ : σ, we say define validity of refinement, ⊨ r ⊑_{σ} r′ as: for all c′ ⊨ r′, there exists c ⊨ r such that c =_{σ} c′.

Remark 6.3.1 We say that a λ_{?}-axiom system is operationally complete if whenever a refinement is operationally valid, then it is provable. An example of a signature which is not operationally complete was given in Example 3.4.4. We have ⊨ λn : nat.let y : nat in succ y ⊑ id_{pos}, where id_{pos} is the identity on positive naturals λn : nat.cond(eq(n, 0), 1, n), but without any form of recursion we cannot define a predecessor term and actually prove the refinement. It is important in practice to ensure that we only use operationally complete signatures, so as to avoid writing specifications which can not be implemented, yet are intuitively implementable.

We saw that λ_{?}-axiom systems are (denotationally) complete with respect to a class of Henkin models with a factoring condition. It might be that operationally • c ⊨ ∗ when c ≡ ∗

• c ⊨ ⟨r_{1}, r_{2}⟩ when c ≡ ⟨c_{1}, c_{2}⟩ and c_{1} ⊨ r_{1}, c_{2} ⊨ r_{2}

• c ⊨ λx : σ.r when c ≡ λx : σ.t, for all c′ : σ, t[c′] ⇓ d and d ⊨ r[c′]

• c ⊨ ?_{σ} when c : σ

• c ⊨ π_{1}(r) when there exists c′ such that ⟨c, c′⟩ ⊨ r

• c ⊨ π_{2}(r) when there exists c′ such that ⟨c′, c⟩ ⊨ r

• c ⊨ r_{1}r_{2} when there exists λx : σ.t ⊨ r_{1}, c_{2} ⊨ r_{2}, such that c ⊨ t[c_{2}/x]

• c ⊨ let x : σ be r in r′ when there exists c′ ⊨ r such that c ⊨ r′[c′/x] Figure 6.2: Satisfaction

∗ =_{1} ∗
⟨c_{1}, c_{2}⟩ =_{σ×τ} ⟨c′_{1}, c′_{2}⟩ when c_{1} =_{σ} c′_{1} and c_{2} =_{τ} c′_{2}

λx : σ.t =_{σ→τ} λx : σ.t′ when for all c : σ, for all d : τ, d ⊨ t[c/x] iff d ⊨ t′[c/x] Figure 6.3: Equality of Canonical Terms

• ⊨ ⊥ never

• ⊨ P ⊃ P′ when ⊭ P or ⊨ P′

• ⊨ ∀x : σ.P when for all c : σ, ⊨ P [c/x]

• ⊨ r ⊑_{σ} r′ when for all c′ ⊨ r′, there exists c ⊨ r such that c =_{σ} c′. Figure 6.4: Validity of Propositions complete theories are complete for arbitrary Henkin models.

6.3.2 Refinement Types

We can also give an operational semantics to the λ_{(:)}-calculus based on the satisfaction of terms and properties by canonical terms. There are three mutually recursive components to the operational semantics: an extensional equality on canonical terms, c =_{ϕ} c′; the satisfaction of underdetermined terms by canonical terms, c ⊨ t; and the validity of propositions, ⊨ P.

We define extensional equality, c =_{ϕ} c′, by induction on ϕ. Then the satisfaction of refinement types by canonical terms, c ⊨ ϕ, can be defined as c =_{ϕ} c. Now we can define ⊨ t : ϕ as for all c ⊨ t, c ⊨ ϕ. Finally, we say that t =_{ϕ} t′ is operationally valid when for all c ⊨ t, and for all c′ ⊨ t′, c =_{ϕ} c′.

6.4 Annotations

Program reasoning and manipulation often requires facts which are true at some local program point. For example, if it is known that variable n must be within certain bounds, then a programmer (or compiler) may be able to perform some partial evaluation or optimisation.

Annotating program text with propositions was first suggested by Floyd [Flo67] and is now used in many refinement calculi (e.g. [Bun97]) to facilitate reasoning and to express local assumptions.

Extending the type system of λ^{×→} to refinement types gives a simple notion of program annotation, where variables on abstractions are labelled with logical information. Although we do not have explicit annotations in our calculus, we can define certain forms. For r : ϕ, define

assertion r | (x : ϕ)P = ?_{(x:ϕ) r ⊑_{ϕ} x ∧ P}
guard  (x : ϕ)P → r = ?_{(x:ϕ) P ⊃ r ⊑_{ϕ} x}

As we showed in Remark 4.3.22, we can also combine guards with the refinement types. For example:

P → (x : ϕ)Q ≡ (x : (P → ϕ))P ⊃ Q
P → (ϕ → ψ) ≡ (P → ϕ) → (P → ψ)

Assertions could be treated similarly.

We could, however, consider a calculus with true annotations. One possibility would be to extend the simply-typed lambda calculus with terms of the form P → t and t|P where P is a proposition, and operational meanings

c ⊨ P → t when ⊨ P implies c ⊨ t
c ⊨ t|P when ⊨ P and c ⊨ t

Thus if ⊭ P, any c (of appropriate type) will satisfy P → t.

We could define a (meaning preserving?) translation ( )^{∘} from a subset of λ_{(:)} to the annotation calculus, with

(λx : σ |P.t)^{∘} = λx : σ.P → t^{∘}

An alternative formulation of the refinement calculus would be to take annotations as primitive. Then we could define ‘set-theoretic’ (as opposed to pertheoretic) specifications as

?_{(x:τ)P} = let x : τ in (x|P)

6.5 Search Calculi

The thesis of Pym [Pym90] presents a theory of proof search. One idea developed there (and also in [PW90]) is to give a hierarchy of calculi each of which can be regarded as the metatheory of the next and in which the search space for proofs is increasingly constrained. This idea could be applied to the present work on refinement.

Refinement is traditionally formulated as a generalised equality and, as pointed out after Lemma 3.2.17, this is also the style of the λ_{?}- and λ_{⊑}-calculi. The refinement of specification ϕ to program t, ϕ ⊑ t, is thought of as “t is less than ϕ in the refinement ordering”. The calculi do not contain rules for directing a search. Now we commented in Section 1.4 that we could consider a search-oriented (rather than equational) refinement calculus. Then t is seen as a solution to the search for a program to satisfy ϕ.

In Section 5.5 we defined a number of auxiliary relations. The relationship between ⊑_{ϕ} and ⇝ is interesting because it mirrors the difference between refinement and search. Following [PW90], we could consider a hierarchy of subsystems of the full equational refinement theory. For example:

Γ, x : σ ⇒ σ Γ ⇒ 1

\begin{matrix}Γ ⇒ σ & Γ ⇒ τ \\ Γ ⇒ σ × τ \end{matrix}  {Γ, y : σ ⇒ τ \atop Γ ⇒ σ → τ}

{Γ, x : τ, y : τ′ ⇒ σ \atop Γ, z : τ × τ′ ⇒ σ}

{Γ, x : τ′ ⇒ σ \atop Γ, y : τ → τ′, z : τ ⇒ σ}

Figure 6.5: Type Inhabitation

1. Γ ⇒ σ (type inhabitation)

2. Γ ⊢ r ⇝ t (coding)

3. Γ ⊢ r ⊑^{s} t (simple refinement)

4. Γ ⊢ r ⊑ t (nonlogical refinement)

We consider first the simple task of finding a program to inhabit a type. Figure 6.5 presents backward oriented rules which may be used to automate such a search. These rules can be viewed as reformulations of refinements of the form Γ ⊢ ?_{σ} ⊑_{σ} t, where t could be read off the rules, or included as a labelled deduction.

We can then consider rules for proving inhabitation of an arbitrary r. This coding can be thought of as a ‘big step’ refinement. The rules for equality can be omitted from ⊑^{s} as equality is orthogonal to refinement in the sense that if r ⊑ t, then there exists t′ such that r ⊑^{s} t′, and t = t′.

This system can then be embedded in a more general system for proving refinements of the form r ⊑ t, where instead of thinking of refinement as a search for inhabitation of a specification, we think of it as a generalised equality. Indeed, we could view the refinement calculus as being a means of representing stages in the search for inhabitants of specifications, and so as a metatheory for a search calculus.

We could also study the difference between equational and search-oriented refinement from a semantic viewpoint. Perhaps a search calculus could be modelled using a possible worlds semantics.

6.6 Logical Variables

A related idea to the distinction between equational and search-oriented refinement is presenting the refinement calculus in both natural deduction and sequent calculus styles. We chose what is essentially a sequent calculus presentation of natural deduction because it is better suited to proof search. However, as pointed out in the previous section, the calculus is not search-oriented anyway. A natural deduction style presentation would be clearer, though. Hence, it might be best to give the equational theory in a natural deduction style, translate a search-oriented calculus into the sequent calculus style, and then prove them equivalent. We could present the refinement calculus in true sequent style using logical variables.

This would effectively be a unification of the two paradigms of refinement, in the sense that any search theory gives rise to an associated equational theory, and the rules of any equational theory can be restricted to a search-oriented subset.

6.7 Second Order: Data Refinement

An extension to the second order (polymorphic) lambda calculus offers hope of combining program and data refinement in one formalism, as well as allowing specification by observational equivalence.

The account of specifications in Chapter 4 which brings equality to the fore should be especially useful in data refinement, where it is natural to consider different equalities at the abstract and concrete types. Moreover, the combination of refinement types and existential variables would be a natural way of augmenting the work in [MP88] with equations.

It will be interesting to see how the calculi can be extended to the second order. This should reduce the number of rules through the impredicative encodings of unit and product (as well as sum). More importantly, the calculus would then be able to express inductive types and iteration. We ought to get derived refinement rules for data types like nat and list[X].

We can define abstract data types using existential types. The question of the connection between this view of data refinement and that of the methodologists (such as Back and Morgan) and the categorical studies of Hoare [Hoa87] and Tennent et.al. [KOP^{+}97] then arises. The use of parametric polymorphism might throw some light on the use of relations in model-oriented data refinement.

6.8 Full Recursion

The introduction of nontermination (using say, Plotkin’s computational metalanguage) will raise particular issues. However, it should be possible to integrate nontermination smoothly. Our modular approach should help in tackling this problem.

Traditional type-theoretic approaches (such as [NPS90]) cannot handle nontermination since all terms terminate. For example, naively adding full recursion to the simply-typed lambda calculus results in inconsistency (see p. 112). However, λ_{(:)} is ‘type-theoretic’ without maintaining a Curry-Howard isomorphism.

Many complications arise with nontermination when underdeterminism is modelled as nondeterminism. This is most clearly seen from a semantic point of view. Such models are based on powerdomains. However, simple sets (of interpretations of determined terms, possibly nonterminating) would seem to suffice here. This reflects the intuition that underdeterminism is something ‘above’ computation and does not ‘interact’ with it.

By contrast, the powerdomain approaches raise a myriad questions concerning how exactly the ? and ⊥ interact. For example, some authors have made a distinction between erratic, demonic and angelic nondeterminism, but it is not clear that these notions transfer to underdeterminism.

As for extending λ_{(:)} to nontermination, the distinction between partial and total correctness then arises (and similarly for refinement in λ_{⊑}). Although we could still model specifications with pers, we might possibly want to add some condition such as downward closure. This does not contradict the comment above that nontermination should not interact with underdeterminism because (presumably) we would still model λ_{⊑}-terms as sets of equivalence classes.

Some other questions we might address are the interaction between refinement types and recursion (e.g. whether we should have μx: ϕ.r : μx: ϕ.ψ[x]), and how do we ensure progress during recursive refinement (i.e. avoiding refining to ⊥). Recursion at the level of specifications is a separate issue. It is possible that work on subtyping systems for recursive types would be useful there.

6.9 Program Transformation

To many people, “program refinement” and “program transformation” are synonyms. While the basic idea of either can be generalised to include the other, it is useful to draw a distinction between refinement of a logical specification into concrete code, and transformation of concrete program code into ‘better’ code. This separation of concerns corresponds to the idea that a (particular) program development calculus based on the simply-typed lambda calculus can be factored into two orthogonal extensions to the lambda calculus, where program development consists of two stages: developing functionally correct code which satisfies an extensional specification, and then the application of optimising transformations to produce efficient code.

Reconciling these two approaches would address the common concern that an emphasis on methodologies for developing structured programs can result in inefficient code, while optimal programs tend to be hard to understand. Instead, both approaches can be combined: programs can be constructed by refinement from a specification, and then optimised using transformations. As the transformations, and indeed the refinements, are recorded, it is possible to view a program at various levels of abstraction, from non-optimal but clear code to logical specification.

Rather than just allow arbitrary, and possibly incorrect, transformations, we could give a transformation calculus, where an applied theory consists of a particular intensional feature, such as time complexity, and a collection of atomic transformations which respect this. The intensional feature is incorporated as an extended type and transformation rules are generated from the atomic rules.

An interesting variety of intensional features could be incorporated into this framework. For example, work on formalising program style could perhaps be recast in this way.

The ability to express equality at a refinement type is useful in program transformation. For example, we might want to transform a function of type nat → nat, with the prescription “maintain value on evens, and improve on odds (in some way)”. We can express (part of) this by saying that the terms are equal at the refinement type even → nat.

6.10 Abstract Viewpoint

The semantics of refinement calculi could be extended to a more general categorical framework. Previous work has tended to characterise refinement in terms of either inclusion of models, or of preservation of properties, from which proof rules are then derived. We took the opposite approach by giving an explicit axiomatisation of refinement in order to get a tractable syntactic definition.

Nevertheless, there is the question of an abstract characterisation in a general semantic framework. For example, the factorisation of refinement in λ_{⊑} should have some semantic counterpart. A significant motivation for carrying this out is that a general notion of model offers some hope of making connections between refinement and popular development methodologies.

Hermida [Her93] uses fibrations to model predicates over λ^{×→} (but not for any more complicated type theories). He uses fibrations with indeterminates to model parameterisation. Models of many calculi can be presented as fibrations. For example, a dependently-typed calculus could be modelled by two fibrations — one to handle the dependency and the other the logic. Underdeterminism should be a separate feature on top of this set-up.

We should be able to characterise ⊑_{ϕ} semantically, using the specialisation order for example, as in [Fio94]. Whether derived or assumed, there is a posetenriched structure where the ordering of morphisms corresponds to refinement.

More generally, we could envisage a 2-categorical structure, where the 2-cells correspond to proof of refinement. The let congruence rules give a 2-categorical structure. If we interpret Γ ⊢ r ⊑_{ϕ} r′ as a 2-cell, α, from [[Γ ⊢ r : ϕ]] to [[Γ ⊢ r′ : ϕ]], and β = [[Γ ⊢ r′ ⊑_{ϕ} r′′]], γ = [[x : ϕ ⊢ s ⊑ s′]], δ = [[x : ϕ ⊢ s ⊑ s′′]], then we can define γ ⋆ α to be

[[Γ ⊢ let x : ϕ be r in s ⊑ let x : ϕ be r′ in s′]]

Because of the congruence rules for let, this is a valid refinement. The interchange law, that (δ ⋆ β) ∘ (γ ⋆ α) = (δ ∘ γ) ⋆ (β ∘ α), then follows since both terms correspond to the same refinement.

6.11 Aspects of the Software Life-cycle

Most theoretical work on formal methods has been on program verification and development. However, the software life-cycle consists of many other activities, and a comprehensive theory should include these. We make some suggestions for how our work on refinement could be extended to some other related areas.

6.11.1 Prototyping

The calculus formalises partially developed programs as combinations of specification and code, and induces a logic for them from a logic on the underlying programming language. Hence it is possible to reason about partially developed programs as ‘first-class’ artifacts even though, in general, it is not possible to evaluate arbitrary combinations of specification and program code. It is possible, however, to give an operational semantics to the language so that terms can be evaluated in certain situations as if they were programs.

This idea was the thinking behind the dynamic semantics of Extended ML (section 5.4.1), so it would be interesting to investigate this simple notion of prototyping further, and see what possibilities this offers for specification testing.

6.11.2 Maintenance

The problem of software maintenance is to modify legacy code which performs some function, so that it performs some related function. This can be formulated using the language of refinement. Given a program t which satisfies specification ϕ, and another specification ϕ′, which bears some relationship to ϕ, the problem is to construct a program which satisfies ϕ′.

By an appropriate formalisation of how ϕ′ relates to ϕ, it should be possible to automatically construct a partially developed program, r, which is t with the code that needs to be rewritten replaced by the appropriate specifications. More ambitiously, it should be possible to reuse the refinement itself, so that part of the refinement of ϕ to t can be used in constructing a refinement from ϕ′ to r.

This idea of extracting part of a term based on a specification is similar to program slicing, and we could investigate connections with this view of maintenance.

6.11.3 Reverse Engineering

The general idea of reverse engineering is to recover a high-level description from an actual implementation. This is useful for both comprehension and maintenance. Some researchers have considered the problem of reverse engineering a program t to specification ϕ [War88]. This appears, at first sight anyway, to be dual to the problem of refinement from ϕ to t so perhaps some formal connections can be made.

Appendix A Notation

Refinement Terms  r
Determined Terms  t
Total Terms  u
Individual Variables  x, y, z
Function Variables  f, g, h
Boolean Variables  b
Natural Number Variables  n
Variable Contexts  Γ
Propositional Contexts  Δ
Propositions  P, Q, R
Ground Types  γ
Types  σ, τ, υ
Refinement Types  ϕ, ψ, χ
Pseudotypes  κ
Basic Judgements  B
Judgements  J
Expressions  U, V
Constant Symbols  k
Predicate Symbols  F
Henkin Interpretations, Models  A, T
Sets  A
Individuals  a, b
Mappings  m
Syntactic Environments  g
Environments  η
Relations  R, Φ
Theories  T

Bibliography

[AC96] David Aspinall and Adriana Compagnoni. Subtyping dependent types. In Proceedings of the eleventh IEEE Symposium on Logic in Computer Science, 1996.

[Asp95] David Aspinall. Subtyping with singleton types. In Proceedings of Computer Science Logic ‘94, volume 933 of LNCS, 1995.

[Asp97] David Aspinall. Type Systems for Modular Programs and Specifications. PhD thesis, Department of Computer Science, University of Edinburgh, 1997.

[Bac88] R. J. R Back. A calculus of refinements for program derivations. Acta Informatica, 25:593–624, 1988.

[BB95] M. A. Bednarczyk and T. Borzyszkowski. Towards program development, specification and verification with Isabelle. In Isabelle Users Workshop, University of Cambridge, 1995.

[BM92] R. Burstall and J. McKinna. Deliverables: A categorical approach to program development in type theory. In Mathematical Foundations of Computer Science: 18th International Symposium, volume 711 of Lecture Notes in Computer Science, pages 32–67, 1992. An earlier version appeared as LFCS Technical Report ECS-LFCS-92-242.

[Bos95] J. Bos. Predicate logic unplugged. In Tenth Amsterdam Colloquium, 1995.

[Bun97] Alex Bunkenburg. Expression Refinement. PhD thesis, Department of Computing Science, University of Glasgow, 1997.

[Bur92] G. L. Burn. A logical framework for program analysis. In J. Launchbury and P. Sansom, editors, Proceedings of the 1992 Glasgow Functional Programming Workshop, pages 30–42. Springer-Verlag Workshops in Computer Science series, 6–8 July 1992.

[CDG96] Mario Coppo, Ferruccio Damiani, and Paola Giannini. Refinement types for program analysis. In Static analysis: third International Symposium, SAS ’96, volume 1145 of Lecture Notes in Computer Science, pages 143–158. Springer-Verlag, 1996.

[Cro93] Roy L. Crole. Categories for Types. Cambridge Mathematical Textbooks. Cambridge University Press, 1993.

[Den97a] Ewen Denney. Refining Refinement Types. In Informal Proceedings of Types Workshop on Subtyping, Inheritance and Modular Development of Proofs, University of Durham, 1997.

[Den97b] Ewen Denney. Simply-typed Underdeterminism. In EU KIT/ IOS International Workshop on Formal Models of Programming and their Applications, Institute of Software, Beijing, 1997. To appear in special issue of Journal of Computer Science and Technology.

[Den98] Ewen Denney. Refinement Types for Specification. In David Gries and Willem-Paul de Roever, editors, IFIP Working Conference on Programming Concepts and Methods (PROCOMET ’98), Shelter Island, New York, USA, pages 148–166. Chapman and Hall, 1998.

[Dij76] E. W. Dijkstra. A Discipline of Programming. Prentice-Hall International, 1976.

[Fef85] Solomon Feferman. A theory of variable types. In Proceedings of the Fifth Latin American Symposium on Mathematical Logic, volume 19 of Revista Colombiana de Matemáticas, 1985.

[Fio94] Marcelo Fiore. Axiomatic Domain Theory in Categories of Partial Maps. PhD thesis, Department of Computer Science, University of Edinburgh, 1994.

[Flo67] Robert W. Floyd. Assigning meanings to programs. In J. T. Schwarz, editor, Proc. Symp. in Applied Mathematics, pages 19–32, 1967.

[FP91] Tim Freeman and Frank Pfenning. Refinement types for ML. In Proceedings of the SIGPLAN’91 Symposium on Language Design and Implementation, pages 268–277. ACM Press, 1991.

[Har79] David Harel. First-Order Dynamic Logic. Lecture Notes in Computer Science. Springer-Verlag, 1979.

[Har80] David Harel. Proving the correctness of regular deterministic programs: A unifying survey using dynamic logic. Theoretical Computer Science, 12:61–81, 1980.

[Hay94a] Susumu Hayashi. Logic of refinement types. In Types for Proofs and programs, volume 806 of Lecture Notes in Computer Science. Springer Verlag, 1994.

[Hay94b] Susumu Hayashi. Singleton, union, and intersection types for program extraction. Information and Computation, 109:174–210, 1994.

[Her93] Claudio Hermida. Fibrations, Logical Predicates and Indeterminates. PhD thesis, Department of Computer Science, University of Edinburgh, 1993.

[HJ95] Claudio Hermida and Bart Jacobs. Fibrations with indeterminates: Contextual and functional completeness for polymorphic lambda calculi. Mathematical Structures in Computer Science, 5(4), 1995.

[Hoa87] C. A. R. Hoare. Data refinement in a categorical setting. Unpublished manuscript, 1987.

[Jen91] Thomas Jensen. Strictness analysis in logical form. In J. Hughes, editor, Proceedings of the Conference on Functional Programming and Computer Architecture, volume 523 of LNCS, pages 352–366, 1991.

[Jon90] Cliff B. Jones. Systematic Software Development using VDM. Prentice Hall International, 1990.

[JS91] Geraint Jones and Mary Sheeran. Relations and refinement in circuit design. In Carroll Morgan and Jim Woodcock, editors, 3rd Refinement Workshop 1990, Springer Workshops in Computing, 1991.

[KOP^{+}97] Y. Kinoshita, P. W. O’Hearn, A. J. Power, M. Takeyama, and R. D. Tennent. An axiomatic approach to binary logical relations with applications to data refinement. Lecture Notes in Computer Science, 1281, 1997.

[KST97] Stefan Kahrs, Donald Sannella, and Andrzej Tarlecki. The definition of Extended ML: A gentle introduction. Theoretical Computer Science, 173:445–484, 1997.

[Lei69] A. C Leisenring. Mathematical Logic and Hilbert’s ϵ-Symbol. University Mathematical Series. MacDonald Technical and Scientific, 1969.

[LP92] Z. Luo and R. Pollack. LEGO Proof Development System: User’s Manual. Technical Report ECS-LFCS-92-211, Department of Computer Science, University of Edinburgh, 1992.

[Luo91] Z. Luo. Program specification and data refinement in type theory. LFCS Technical Report ECS-LFCS-90-131, Department of Computer Science, University of Edinburgh, 1991.

[Mar96] Per Martin-Löf. On the meanings of the logical constants and the justifications of the logical laws. Nordic Journal of Philosophical Logic, 1(1):11–60, 1996.

[McK92] James McKinna. Deliverables: A Categorical Approach to Program Development in Type Theory. PhD thesis, Department of Computer Science, University of Edinburgh, 1992.

[Mit96] J. Mitchell. Foundations for Programming Languages. Foundations of Computing Series. MIT Press, 1996.

[MM91] J. C. Mitchell and E. Moggi. Kripke-style models for typed lambda calculus. Annals of Pure and Applied Logic, 51:99–124, 1991. Preliminary Version in Proc. IEEE Symposium on Logic in Computer Science, 1987, pages 303-314.

[MMMS87] A. R. Meyer, J. C. Mitchell, E. Moggi, and R. Statman. Empty types in polymorphic lambda calculus. In Proc. 14th ACM Symposium on Principles of Programming Languages, pages 253–262, 1987. Reprinted with minor revisions in Logical Foundations of Functional Programming, ed. G. Huet, Addison-Wesley (1990), pages 273-284.

[Mog91] E. Moggi. Notions of computation and monads. Information and Computation, 1, 1991.

[Mor87] J. Morris. A theoretical basis for stepwise refinement and the programming calculus. Science of Computer Programming, 9:287–306, 1987.

[Mor94] C. Morgan. Programming from Specifications. Prentice Hall, 1994.

[MP88] 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.

[NH92] T. S. Norvell and E. C. R. Hehner. Logical specifications for functional programs. In Mathematics of Program Construction, volume 669 of Lecture Notes in Computer Science, 1992.

[Nie96] Flemming Nielson. Annotated type and effect systems. ACM Computing Surveys, 28(2):344–345, June 1996.

[NN88] Hanne Nielson and Flemming Nielson. Automatic binding time analysis for a typed λ-calculus. In Proceedings of the Fifteenth Annual ACM Symposium on Principles of Programming Languages, 1988.

[Nor88] Bengt Nordström. Terminating general recursion. Bit, 28:605–619, 1988.

[NPS90] B. Nordström, K. Petersson, and J. M. Smith. Programming in Martin-Löf’s Type Theory, volume 7 of Monographs on Computer Science. Oxford University Press, 1990.

[Pfe93] Frank Pfenning. Refinement types for logical frameworks. In Herman Geuvers, editor, Informal Proceedings of the Workshop on Types for Proofs and Programs, pages 285–299, 1993.

[Pfe96] Frank Pfenning. The practice of logical frameworks. In Hélène Kirchner, editor, Proceedings of the Colloquium on Trees in Algebra and Programming, volume 1059 of Lecture Notes in Computer Science, pages 119–134, 1996. Invited talk.

[Pit95] A. M. Pitts. Categorical logic. Technical Report 367, University of Cambridge Computer Laboratory, May 1995. 94 pages.

[PW90] David Pym and Lincoln Wallen. Investigations into proof-search in a system of first-order dependent function types. In Proceedings of the 10th International Conference on Automated Deduction, volume 449 of Lecture Notes in Artificial Intelligence, pages 236–250. Springer-Verlag, 1990. Also University of Edinburgh LFCS Report ECS-LFCS-90-111.

[Pym90] David Pym. Proofs, Search and Computation in General Logic. PhD thesis, Department of Computer Science, University of Edinburgh, 1990.

[Ros39] B. Rosser. On the Consistency of Quine’s New Foundations for Mathematical Logic. Journal of Symbolic Logic, 4, 1939.

[San91] Donald Sannella. Formal program development in Extended ML for the working programmer. In Proc. 3rd BCS/FACS Workshop on Refinement, Workshops in Computing, pages 99–130. Springer, 1991.

[SE84] Elliot Soloway and Kate Ehrlich. Empirical studies of programming knowledge. IEEE Transactions on Software Engineering, SE-10(5):595–609, 1984.

[Spi92] J. M. Spivey. The Z Notation: A Reference Manual. Prentice Hall International, 2nd edition, 1992.

[SS83] William L. Scherlis and Dana S. Scott. First steps towards inferential programming. In R. E. A. Mason, editor, Information Processing 83: Proceedings of the IFIP 9th World Computer Congress, 1983.

[SST92] Donald Sannella, Stefan Sokołowski, and Andrzej Tarlecki. Toward formal development of programs from algebraic specifications: Parameterisation revisited. Acta Informatica, 29(8):689–736, 1992.

[ST87] Don Sannella and Andrzej Tarlecki. Toward formal development of programs from algebraic specifications: implementations revisited. In Proc. Joint Conf. on Theory and Practice of Software Development, volume 249 of LNCS, pages 96–110. Springer, 1987. Extended abstract.

[Tal90] Carolyn Talcott. A theory for program and data type specification. Theoretical Computer Science, 1990. Disco90 special issue.

[vD94] Dirk van Dalen. Logic and Structure. Springer-Verlag, 1994.

[vL90] Jan van Leeuwen, editor. Handbook of Theoretical Computer Science, volume B: Formal Models and Semantics. Elsevier: MIT Press, 1990.

[War88] Martin Ward. Transforming a program into a specification. Technical Report TR-88, Centre for Software Maintenance, University of Durham, January 1988.

[War94] Nigel Ward. A Refinement Calculus for Nondeterministic Expressions. PhD thesis, University of Queensland, 1994.

[Wir71] Niklaus Wirth. Program development by stepwise refinement. Communications of the ACM, 14(4):221–227, 1971.

Index

abstract programs, 7 algebraic specification, 14, 27 annotations, 118, 202 axiom system

λ_{?}-, 57 λ_{(:)}-, 98 λ_{⊑}-, 139 λ^{×→}-, 35 first-order λ_{?}-, 84 first-order λ^{×→}-, 44

bottom-up, 71

coding, 73, 176 coercion, 176 computational lambda calculus, 54 consistent, 128 correctness, 10, 12

data refinement, 21, 205 determined, 54, 138

environment model condition, 41 existential variables, 162 extensional, 12, 40, 92, 95

factoring condition, 76 full set-theoretic function hierarchy,

77

generation lemma, 168

Henkin interpretation λ_{?}-, 76

λ_{(:)}-, 120

λ_{⊑}-, 180
λ^{×→}-, 41
first-order λ^{×→}-, 48

Henkin model λ_{?}-, 78

λ_{(:)}-, 124
λ_{⊑}-, 181
λ^{×→}-, 41
first-order λ^{×→}-, 48

Henkin theory λ_{(:)}-, 126 λ_{⊑}-, 188 first-order λ_{?}-, 85 first-order λ^{×→}-, 49

maximal refinement type, 132

nondeterminism, 7, 54, 63, 70, 206

partiality, 158 power types, 97 problem reduction, 8 program analysis, 12 program logic, 12 pseudotypes, 116

quotient, 196

recursive refinement, 147, 206 refinement terms, 53 refinement types, 89 rough types, 117

satisfiable, 62, 128 search, 203 signature

λ_{?}-, 53 λ_{(:)}-, 94 λ_{⊑}-, 137 λ^{×→}-, 33 constant, 33 first-order λ_{?}-, 83 first-order λ^{×→}-, 44 type, 32

singleton types, 106, 197 skeleton, 21 stub, 6 subset types, 162 syntactic environment, 34

total, 95 transformation, 21

underdetermined, 54 underdeterminism, 7, 52

well-structured, 117 wide-spectrum, 7, 21