Chapter 3

Formalization in a Logical Framework

We can look at the current field of problem solving by computers as a series of ideas about how to present a problem. If a problem can be cast into one of these representations in a natural way, then it is possible to manipulate it and stand some chance of solving it.

— Allen Newell,

Limitations of the Current Stock of Ideas for Problem Solving [New65]

In the previous chapter we have seen a typical application of deductive systems to specify and prove properties of programming languages. In this chapter we present techniques for the formalization of the languages and deductive systems involved. In the next chapter we show how these formalization techniques can lead to implementations.

The logical framework we use in these notes is called LF and sometimes ELF (for Edinburgh Logical Framework).^{1} LF was introduced by Harper, Honsell, and Plotkin [HHP93]. It has its roots in similar languages used in the project Automath [dB80]. LF has been explicitly designed as a meta-language for high-level specification of languages in logic and computer science and thus provides natural support for many of the techniques we have seen in the preceding chapter. For example, it can capture the convention that expressions which differ only in the names of bound variables are identified. Similarly, it allows direct expression of contexts and variable lookup as they arise in the typing judgment. The fact that these techniques are directly supported by the logical framework is not just a matter of engineering an implementation of the deductive systems in question, but it will be a crucial factor for the succinct implementation of proofs of meta-theorems such as type preservation.

1This is not to be confused with Elf, which is the programming language based on the LF logical framework we introduce in Chapter 4.

By codifying formalization techniques into a meta-language, a logical framework also provides insight into principles of language presentation. Just as it is interesting to know if a mathematical proof depends on the axiom of choice or the law of excluded middle, a logical framework can be used to gauge the properties of the systems we are investigating.

The formalization task ahead of us consists of three common stages. The first stage is the representation of abstract syntax of the object language under investigation. For example, we need to specify the languages of expressions and types of Mini-ML. The second stage is the representation of the language semantics. This includes the static semantics (for example, the notion of value and the type system) and the dynamic semantics (for example, the operational semantics). The third stage is the representation of meta-theory of the language (for example, the proof of type preservation). Each of these uses its own set of techniques, some of which are explained in this chapter using the example of Mini-ML from the preceding chapter.

In the remainder of this chapter we introduce the framework in stages, always motivating new features using our example. The final summary of the system is given in Section 3.8 at the end of this chapter.

3.1 The Simply-Typed Fragment of LF

For the representation of the abstract syntax of a language, the simply-typed λcalculus (λ^{→}) is usually adequate. When we tackle the task of representing inference rules, we will have to refine the type system by adding dependent types. The reader should bear in mind that λ^{→} should not be considered as a functional programming language, but only as a representation language. In particular, the absence of recursion will be crucial in order to guarantee adequacy of representations. Our formulation of the simply-typed λ-calculus has two levels: the level of types and the level of objects, where types classify objects. Furthermore, we have signatures which declare type and object constants, and contexts which assign types to variables. Unlike Mini-ML, the presentation is given in the style of Church: every object will have a unique type. This requires that types appear in the syntax of objects to resolve the inherent ambiguity of certain functions, for example, the identity function. We let a range over type constants, c over object constants, x over variables.

Types  A ::= a | A_{1} → A_{2}
Objects  M ::= c | x | λx:A. M | M_{1} M_{2}
Signatures Σ ::= · | Σ, a:type | Σ, c:A
Contexts  Γ ::= · | Γ, x:A

We make the general restriction that constants and variables can occur at most once in a signature or context, respectively. We will write Σ(c) = A if c:A occurs in Σ and Σ(a) = type if a:type occurs in Σ. Similarly Γ(x) = A if x:A occurs in Γ. We will use A and B to range over types, and M and N to range over objects. We refer to type constants a as atomic types and types of the form A → B as function types.

The judgments defining λ^{→} are

⊢_{Σ} A : type A is a valid type
Γ ⊢_{Σ} M : A M is a valid object of type A in context Γ
⊢ Σ Sig  Σ is a valid signature
⊢_{Σ} Γ Ctx  Γ is a valid context

They are defined via the following inference rules.

\begin{aligned}Σ(c) = A \\ & con \\ Γ ⊢_{Σ} c : A \end{aligned}  \begin{aligned}Γ(x) = A \\ & var \\ Γ ⊢_{Σ} x : A \end{aligned}
⊢_{Σ} A : type Γ, x:A ⊢_{Σ} M : B

lam Γ ⊢_{Σ} λx:A. M : A → B

Γ ⊢_{Σ} M : A → B Γ ⊢_{Σ} N : A app

Γ ⊢_{Σ} M N : B

Σ(a) = type ; tcon ; ⊢_{Σ} a : type
⊢_{Σ} A : type ⊢_{Σ} B : type

arrow

⊢_{Σ} A → B : type

\frac{esig}{⊢ · Sig}

⊢ Σ Sig

tconsig

⊢ Σ, a:type Sig

⊢ Σ Sig ⊢_{Σ} A : type

consig

⊢ Σ, c:A Sig

\frac{ectx}{⊢_{Σ} · Ctx}

⊢_{Σ} Γ Ctx ⊢_{Σ} A : type

varctx

⊢_{Σ} Γ, x:A Ctx

The rules for valid objects are somewhat non-standard in that they contain no check whether the signature Σ or the context Γ are valid. These are often added to the base cases, that is, the cases for variables and constants. We can separate the validity of signatures, since the signature Σ does not change in the rules for valid types and objects, Furthermore, the rules guarantee that if we have a derivation D :: Γ ⊢_{Σ} M : A and Γ is valid, then every context appearing in D is also valid. This is because the type A in the lam rule is checked for validity as it is added to the context. For an alternative formulation see Exercise 3.1.

Our formulation of the simply-typed λ-calculus above is parameterized by a signature in which new constants can be declared; only variables, λ-abstraction, and application are built into the language itself. In contrast, our formulation of Mini-ML has only a fixed set of constants and constructors. So far, we have left the dynamic semantics of λ^{→} unspecified. We later consider canonical forms as an analogue to Mini-ML values and conversion to canonical form as an analogue to evaluation. However, every well-typed λ^{→} object has a canonical form, while not every well-typed Mini-ML expression evaluates to a value. Moreover, we will start with a notion of definitional equality rather than an operational semantics. These differences illustrate that the similarity between Mini-ML as a programming language and λ^{→} as a representation language are rather superficial.

The notion of definitional equality for objects in λ^{→}, written as M ≡ N, can be based on three conversions. The first is α-conversion: two objects are considered identical if they differ only in the names of their bound variables. The second is β-conversion: (λx:A. M) N ≡ [N/x]M. It employs substitution [N/x]M which renames bound variables to avoid variable capture. The third is derived from an extensionality principle. Roughly, two objects of functional type should be equal if applying them to equal arguments yields equal results. This can be incorporated by the rule of η-conversion: (λx:A. M x) ≡ M provided x does not occur free in M. The conversion rules can be applied to any subobject of an object M to obtain an object M′ that is definitionally equal to M. Furthermore the relation of definitional equality is assumed to be an equivalence relation. We define the conversion judgment more formally in Section 3.8, once we have seen which role it plays in the logical framework.

3.2 Higher-Order Abstract Syntax

The first task in the formalization of a language in a logical framework is the representation of its expressions. We base the representation on abstract (rather than concrete) syntax in order to expose the essential structure of the object language so we can concentrate on semantics and meta-theory, rather than details of lexical analysis and parsing. The representation technique we use is called higher-order abstract syntax. It is supported by the simply-typed fragment λ^{→} of the logical framework LF. The idea of higher-order abstract syntax goes back to Church [Chu40] and has since been employed in a number of different contexts and guises. Church observed that once λ-notation is introduced into a language, all constructs that bind variables can be reduced to λ-abstraction. If we apply this principle in a setting where we distinguish a meta-language (the logical framework) from an object language (Mini-ML, in this example) then variables in the object language are represented by variables in the meta-language. Variables bound in the object language (by constructs such as case, lam, let, and fix) will be bound by λ in the meta-language. This has numerous advantages and a few disadvantages over the more immediate technique of representing variables by strings; some of the trade-offs are discussed in Section 3.10.

In the development below it is important not to confuse the typing of Mini-ML expressions with the type system employed by the logical framework, even though some overloading of notation is unavoidable. For example, “:” is used in both systems. For each (abstract) syntactic category of the object language we introduce a new type constant in the meta-language via a declaration of the form a:type. Thus, in order to represent Mini-ML expressions we declare a type exp in the meta-language LF.^{2}

exp : type

We intend that every LF object M of type exp represents a Mini-ML expression and vice versa. The Mini-ML constant z is now represented by an LF constant z declared in the meta-language to be of type exp.

z : exp

The successor s is an expression constructor. It is represented by a constant of functional type that maps expressions to expressions so that, for example, s z has type exp.

s : exp → exp

We now introduce the function ⌜·⌝ which maps Mini-ML expressions to their representation in the logical framework. Later we will use ⌜·⌝ generically for representation functions. So far we have

\begin{aligned}⌜z⌝ & = & z \\ ⌜s e⌝ & = & s ⌜e⌝. \end{aligned}

We would like to achieve that ⌜e⌝ has type exp in LF, given appropriate declarations for constants representing Mini-ML expression constructors. The constructs that do not introduce bound variables can be treated in a straightforward manner.

2Since the representation techniques do not change when we generalize from the simply-typed λ-calculus to LF, we refer to the meta-language as LF throughout.

⌜z⌝ = z  z  : exp
⌜s e⌝ = s ⌜e⌝  s  : exp → exp
⌜⟨e_{1}, e_{2}⟩⌝ = pair ⌜e_{1}⌝ ⌜e_{2}⌝  pair : exp → exp → exp
⌜fst e⌝ = fst ⌜e⌝  fst : exp → exp
⌜snd e⌝ = snd ⌜e⌝  snd : exp → exp
⌜e_{1} e_{2}⌝ = app ⌜e_{1}⌝ ⌜e_{2}⌝  app : exp → exp → exp

Traditionally, one might now represent lam x. e by lam ⌜x⌝ ⌜e⌝, where ⌜x⌝ may be a string or have some abstract type of identifier. This approach leads to a relatively low-level representation, since renaming of bound variables, capture-avoiding substitution, etc. as given in Section 2.2 now need to be axiomatized explicitly. Using higher-order abstract syntax means that variables of the object language (the language for which we are designing a representation) are represented by variables in the meta-language (the logical framework). Variables bound in the object language must then be bound correspondingly in the meta-language. As a first and immediate benefit, expressions which differ only in the names of their bound variables will be α-convertible in the meta-language. This leads to the representation

⌜x⌝ = x
⌜lam x. e⌝ = lam (λx:exp. ⌜e⌝)  lam : (exp → exp) → exp.

Recall that LF requires explicit types wherever variables are bound by λ, and free variables must be assigned a type in a context. Note also that the two occurrences of x in the first line above represent two variables with the same name in different languages, Mini-ML and LF.^{3} The four remaining Mini-ML constructs, case, let val, let name, and fix, also introduce binding operators. Their representation follows the scheme for lam, taking care that variables bound in Mini-ML are also bound at the meta-level and have proper scope. For example, the representation of let val x = e_{1} in e_{2} reflects that x is bound in e_{2} but not in e_{1}.

⌜case e_{1} of z ⇒ e_{2} | s x ⇒ e_{3}⌝ = case ⌜e_{1}⌝ ⌜e_{2}⌝ (λx:exp. ⌜e_{3}⌝)
⌜let val x = e_{1} in e_{2}⌝ = letv ⌜e_{1}⌝ (λx:exp. ⌜e_{2}⌝)
⌜let name x = e_{1} in e_{2}⌝ = letn ⌜e_{1}⌝ (λx:exp. ⌜e_{2}⌝)
⌜fix x. e⌝ = fix (λx:exp. ⌜e⌝)

Hence we have

case : exp → exp → (exp → exp) → exp
letv : exp → (exp → exp) → exp
letn : exp → (exp → exp) → exp
fix : (exp → exp) → exp.

3One can allow renaming in the translation, but it complicates the presentation unnecessarily.

As an example, consider the program double from page 16.

⌜fix f. lam x. case x of z ⇒ z | s x′ ⇒ s (s (f x′))⌝
= fix (λf:exp. lam (λx:exp. case x z (λx′:exp. s (s (app f x′)))))

One can easily see that the object on the right-hand side is valid and has type exp, given the constant declarations above.

The next step will be to formulate (and later prove) what this representation accomplishes, namely that every expression has a representation, and every LF object of type exp constructed with constants from the signature above represents an expression. In practice we want a stronger property, namely that the representation function is a compositional bijection, something we will return to later in this chapter in Section 3.3.

Recall that ⊢_{Σ} is the typing judgment of LF. We fix the signature E to contain all declarations above starting with exp:type through fix:(exp → exp) → exp. At first it might appear that we should be able to prove:

1. For any Mini-ML expression e, ⊢_{E} ⌜e⌝ : exp.

2. For any LF object M such that ⊢_{E} M : exp, there is a Mini-ML expression e such that ⌜e⌝ = M.

As stated, neither of these two propositions is true. The first one fails due to the presence of free variables in e and therefore in ⌜e⌝ (recall that object-language variables are represented as meta-language variables). The second property fails because there are many objects M of type exp which are not in the image of ⌜·⌝. Consider, for example, ((λx:exp. x) z) for which it is easy to show that

⊢_{E} (λx:exp. x) z : exp.

Examining the representation function reveals that the resulting LF objects contain no β-redices, that is, no objects of the form (λx:A. M) N.

A more precise analysis later yields the related notion of canonical form. Taking into account free variables and restricting ourselves to canonical forms (yet to be defined), we can reformulate the proposition expressing the correctness of the representation.

1. Let e be a Mini-ML expression with free variables among x_{1}, …, x_{n}. Then x_{1}:exp, …, x_{n}:exp ⊢_{E} ⌜e⌝ : exp, and ⌜e⌝ is in canonical form.

2. For any canonical form M such that x_{1}:exp, …, x_{n}:exp ⊢_{E} M : exp there is a Mini-ML expression e with free variables among x_{1}, …, x_{n} such that ⌜e⌝ = M.

It is a deep property of LF that every valid object is definitionally equal to a unique canonical form. Thus, if we want to answer the question which Mini-ML expression is represented by a non-canonical object M of type exp, we convert it to canonical form M′ and determine the expression e represented directly by M′.

The definition of canonical form is based on two observations regarding the inversion of representation functions, ⌜·⌝ in the example. The first is that if we are considering an LF object M of type exp we can read off the top-level constructor (the alternative in the definition of Mini-ML expressions) if the term has the form c M_{1} … M_{n}, where c is one of the LF constants in the signature defining Mini-ML expressions. For example, if M has the form (s M_{1}) we know that M represents an expression of the form s e_{1}, where M_{1} is the representation of e_{1}.

The second observation is less obvious. Let us consider an LF object of type exp → exp. Such objects arise in the representation, for example, in the second argument to letv, which has type exp → (exp → exp) → exp. For example,

⌜let val x = s z in ⟨x, x⟩⌝ = letv (s z) (λx:exp. pair x x).

The argument (λx:exp. pair x x) represents the body of the let-expression, abstracted over the let-bound variable x. Since we model the scope of a bound variable in the object language by the scope of a corresponding λ-abstraction in the meta-language, we always expect an object of type exp → exp to be a λ-abstraction. As a counterexample consider the object

letv (pair (s z) z) fst

which is certainly well-typed in LF and has type exp, since fst : exp → exp. This object is not the image of any expression e under the representation function ⌜·⌝. However, there is an η-equivalent object, namely

letv (pair (s z) z) (λx:exp. fst x)

which represents let val x = ⟨s z, z⟩ in fst x.

We can summarize these two observations as the following statement constraining our definition of canonical forms.

1. A canonical object of type exp should either be a variable or have the form c M_{1} … M_{n}, where M_{1}, …, M_{n} are again canonical; and

2. a canonical object of type exp → exp should have the form λx:exp. M_{1}, where M_{1} is again canonical.

Returning to an earlier counterexample, ((λx:exp. x) z), we notice that it is not canonical, since it is of atomic type (exp), but does not have the form of a constant applied to some arguments. In this case, there is a β-equivalent object which is canonical form, namely z. In general each valid object has a βη-equivalent object in canonical form, but this is a rather deep theorem about LF.

For the representation of more complicated languages, we have to generalize the observations above and allow an arbitrary number of type constants (rather than just exp) and allow arguments to variables. We write the general judgment as

Γ ⊢_{Σ} M ⇑ A  M is canonical of type A.

This judgment is defined by the following inference rules. Recall that a stands for constants at the level of types.

⊢_{Σ} A : type Γ, x:A ⊢_{Σ} M ⇑ B

carrow

Γ ⊢_{Σ} λx:A. M ⇑ A → B

Σ(c) = A_{1} → ⋯ → A_{n} → a Γ ⊢_{Σ} M_{1} ⇑ A_{1} … Γ ⊢_{Σ} M_{n} ⇑ A_{n} conapp

Γ ⊢_{Σ} c M_{1} … M_{n} ⇑ a

Γ(x) = A_{1} → ⋯ → A_{n} → a Γ ⊢_{Σ} M_{1} ⇑ A_{1} … Γ ⊢_{Σ} M_{n} ⇑ A_{n} varapp

Γ ⊢_{Σ} x M_{1} … M_{n} ⇑ a

This judgment singles out certain valid objects, as the following theorem shows.

Theorem 3.1 (Validity of Canonical Objects) Let Σ be a valid signature and Γ a context valid in Σ. If Γ ⊢_{Σ} M ⇑ A then Γ ⊢_{Σ} M : A.

Proof: See Exercise 3.2 and Section 3.9. □

The simply-typed λ-calculus we have introduced so far has some important properties. In particular, type-checking is decidable, that is, it is decidable if a given object is valid. It is also decidable if a given object is in canonical form, and every well-typed object can effectively be converted to a unique canonical form. Further discussion and proof of these and other properties can be found in Section 8.2.

3.3 Representing Mini-ML Expressions

In order to obtain a better understanding of the representation techniques, it is worthwile to state in full detail and carry out the proofs that the representation of Mini-ML introduced in this chapter is correct. First, we summarize the representation function and the signature E defining the abstract syntax of Mini-ML.

⌜z⌝ = z
⌜s e⌝ = s ⌜e⌝
⌜case e_{1} of z ⇒ e_{2} | s x ⇒ e_{3}⌝ = case ⌜e_{1}⌝ ⌜e_{2}⌝ (λx:exp. ⌜e_{3}⌝)
⌜⟨e_{1}, e_{2}⟩⌝ = pair ⌜e_{1}⌝ ⌜e_{2}⌝
⌜fst e⌝ = fst ⌜e⌝
⌜snd e⌝ = snd ⌜e⌝
⌜lam x. e⌝ = lam (λx:exp. ⌜e⌝)
⌜e_{1} e_{2}⌝ = app ⌜e_{1}⌝ ⌜e_{2}⌝
⌜let val x = e_{1} in e_{2}⌝ = letv ⌜e_{1}⌝ (λx:exp. ⌜e_{2}⌝)
⌜let name x = e_{1} in e_{2}⌝ = letn ⌜e_{1}⌝ (λx:exp. ⌜e_{2}⌝)
⌜fix x. e⌝ = fix (λx:exp. ⌜e⌝)
⌜x⌝ = x

exp : type
z  : exp
s  : exp → exp
case : exp → exp → (exp → exp) → exp
pair : exp → exp → exp
fst : exp → exp
snd : exp → exp
lam : (exp → exp) → exp
app : exp → exp → exp
let : exp → (exp → exp) → exp
fix : (exp → exp) → exp

Lemma 3.2 (Validity of Representation) For any context Γ = x_{1}:exp, …, x_{n}:exp and Mini-ML expression e with free variables among x_{1}, …, x_{n},

Γ ⊢_{E} ⌜e⌝ ⇑ exp

Proof: The proof is a simple induction on the structure of e. We show three representative cases—the others follow similarly.

Case: e = z. Then ⌜z⌝ = z and Γ ⊢_{E} z ⇑ exp.

Case: e = e_{1} e_{2}. Then ⌜e⌝ = app ⌜e_{1}⌝ ⌜e_{2}⌝. By induction hypothesis there are

derivations

D_{1} :: Γ ⊢_{E} ⌜e_{1}⌝ ⇑ exp, and
D_{2} :: Γ ⊢_{E} ⌜e_{2}⌝ ⇑ exp.

Since E(app) = exp → exp → exp we can apply rule conapp from the definition of canonical forms to D_{1} and D_{2} to conclude that

Γ ⊢_{E} app ⌜e_{1}⌝ ⌜e_{2}⌝ ⇑ exp

is derivable.

Case: e = (let val x = e_{1} in e_{2}). Then ⌜e⌝ = let ⌜e_{1}⌝ (λx:exp. ⌜e_{2}⌝). Note that if e has free variables among x_{1}, …, x_{n}, then e_{2} has free variables among x_{1}, …, x_{n}, x. Hence, by induction hypothesis, we have derivations

D_{1} :: Γ ⊢_{E} ⌜e_{1}⌝ ⇑ exp, and
D_{2} :: Γ, x:exp ⊢_{E} ⌜e_{2}⌝ ⇑ exp.

Applying rule carrow yields the derivation

E(exp) = type

con

⊢_{E} exp : type

D_{2} Γ, x:exp ⊢_{E} ⌜e_{2}⌝ ⇑ exp

carrow

Γ ⊢_{E} λx:exp. ⌜e_{2}⌝ ⇑ exp → exp

Using this derivation, E(let) = exp → (exp → exp) → exp, derivation D_{1} and rule conapp yields a derivation of

Γ ⊢_{E} let ⌜e_{1}⌝ (λx:exp. ⌜e_{2}⌝) ⇑ exp,

which is what we needed to show.

□

Next we define the inverse of the representation function, ⌞·⌟. We need to keep in mind that it only needs to be defined on canonical forms of type exp.

⌞z⌟ = z
⌞s M⌟ = s ⌞M⌟
⌞case M_{1} M_{2} (λx:exp. M_{3})⌟ = case ⌞M_{1}⌟ of z ⇒ ⌞M_{2}⌟ | s x ⇒ ⌞M_{3}⌟
⌞pair M_{1} M_{2}⌟ = ⟨⌞M_{1}⌟, ⌞M_{2}⌟⟩
⌞fst M⌟ = fst ⌞M⌟
⌞snd M⌟ = snd ⌞M⌟
⌞lam (λx:exp. M)⌟ = lam x. ⌞M⌟
⌞app M_{1} M_{2}⌟ = ⌞M_{1}⌟ ⌞M_{2}⌟
⌞letv M_{1} (λx:exp. M_{2})⌟ = let val x = ⌞M_{1}⌟ in ⌞M_{2}⌟
⌞letn M_{1} (λx:exp. M_{2})⌟ = let name x = ⌞M_{1}⌟ in ⌞M_{2}⌟
⌞fix (λx:exp. M)⌟ = fix x. ⌞M⌟
⌞x⌟ = x

Lemma 3.3 For any Γ = x_{1}:exp, …, x_{n}:exp and M such that Γ ⊢_{E} M ⇑ exp, ⌞M⌟ is defined and yields a Mini-ML expression such that ⌜⌞M⌟⌝ = M.

Proof: The proof is by induction on the structure of the derivation D of Γ ⊢_{E} M ⇑ exp. Note that D cannot end with an application of the carrow rule, since exp is atomic.

Case: D ends in varapp. From the form of Γ we know that x = x_{i} for some i and x has no arguments. Hence ⌞M⌟ = ⌞x⌟ = x is defined.

Case: D ends in conapp. Then c must be one of the constants in E. We now further distinguish subcases, depending on c. We only show three subcases; the others follow similarly.

Subcase: c = z. Then c has no arguments and ⌞M⌟ = ⌞z⌟ = z, which is a

Mini-ML expression. Furthermore, ⌜z⌝ = z. Subcase: c = app. Then c has two arguments, ⌞M⌟ = ⌞app M_{1} M_{2}⌟ =

⌞M_{1}⌟ ⌞M_{2}⌟, and, suppressing the premiss E(app) = exp → exp → exp, D has the form

\begin{matrix}D_{1} \\ Γ ⊢_{E} M_{1} ⇑ exp \end{matrix}  \begin{matrix}D_{2} \\ Γ ⊢_{E} M_{2} ⇑ exp \end{matrix}

conapp

Γ ⊢_{E} app M_{1} M_{2} ⇑ exp

By the induction hypothesis on D_{1} and D_{2}, ⌞M_{1}⌟ and ⌞M_{2}⌟ are defined and therefore ⌞M⌟ = ⌞M_{1}⌟ ⌞M_{2}⌟ is also defined. Furthermore, ⌜⌞M⌟⌝ = ⌜⌞M_{1}⌟ ⌞M_{2}⌟⌝ = app ⌜⌞M_{1}⌟⌝ ⌜⌞M_{2}⌟⌝ = app M_{1} M_{2}, where the last equality follows by the induction hypothesis on M_{1} and M_{2}.

Subcase: c = letv. Then c has two arguments and, suppressing the premiss E(letv) = exp → (exp → exp) → exp, D has the form

\begin{matrix}D_{1} \\ Γ ⊢_{E} M_{1} ⇑ exp \end{matrix}  \begin{matrix}D_{2} \\ Γ ⊢_{E} M_{2} ⇑ exp → exp \end{matrix}

conapp

Γ ⊢_{E} letv M_{1} M_{2} ⇑ exp

There is only one inference rule which could have been used as the last inference in D_{2}, namely carrow. Hence, by inversion, D_{2} must have the form

\begin{matrix}D′_{2} \\ Γ, x:exp ⊢_{E} M′_{2} ⇑ exp \end{matrix}

carrow

Γ ⊢_{E} λx:exp. M′_{2} ⇑ (exp → exp)

where M_{2} = λx:exp. M′_{2}. Then

⌞M⌟ = ⌞letv M_{1} (λx:exp. M′_{2})⌟ = (let val x = ⌞M_{1}⌟ in ⌞M′_{2}⌟)

which is a Mini-ML expression by induction hypothesis on D_{1} and D′_{2}. We reason as in the previous cases that here, too, ⌜⌞M⌟⌝ = M.

□

Lemma 3.4 For any Mini-ML expression e, ⌞⌜e⌝⌟ = e.

Proof: The proof is a simple induction over the structure of e (see Exercise 3.3). □

The final lemma of this section is the substitution lemma, which connects metalevel substitution and object-level substitution. We only state this lemma for substitution of a single variable, but other, more general variants are possible. This lemma gives a formal expression to the statement that the representation function is compositional, that is, the representation of a compound expression is constructed from the representations of its immediate constituents. Note that in the statement of the lemma, the substitution on the left-hand side of the equation is substitution in the Mini-ML language as defined in Section 2.2, while on the right-hand side we have substitution at the level of the framework.

Lemma 3.5 (Substitution Lemma) ⌜[e_{1}/x]e_{2}⌝ = [⌜e_{1}⌝/x]⌜e_{2}⌝.

Proof: The proof is by induction on the structure of e_{2}. We show three cases—the remaining ones follow the same pattern.

Case: e_{2} = x. Then

⌜[e_{1}/x]e_{2}⌝ = ⌜[e_{1}/x]x⌝ = ⌜e_{1}⌝ = [⌜e_{1}⌝/x]x = [⌜e_{1}⌝/x]⌜e_{2}⌝.

Case: e_{2} = y and y ≠ x. Then

⌜[e_{1}/x]e_{2}⌝ = ⌜[e_{1}/x]y⌝ = y = [⌜e_{1}⌝/x]y = [⌜e_{1}⌝/x]⌜e_{2}⌝.

Case: e_{2} = (let val y = e′_{2} in e′′_{2}), where y ≠ x and y is not free in e_{1}. Note that this condition can always be achieved via renaming of the bound variable y. Then

= ⌜[e_{1}/x]e_{2}⌝
= ⌜[e_{1}/x](let val y = e′_{2} in e′′_{2})⌝
= ⌜let val y = [e_{1}/x]e′_{2} in [e_{1}/x]e′′_{2}⌝
= letv ⌜[e_{1}/x]e′_{2}⌝ (λy:exp. ⌜[e_{1}/x]e′′_{2}⌝)
= letv ([⌜e_{1}⌝/x]⌜e′_{2}⌝) (λy:exp. [⌜e_{1}⌝/x]⌜e′′_{2}⌝) by induction hypothesis
= [⌜e_{1}⌝/x](letv ⌜e′_{2}⌝ (λy:exp. ⌜e′′_{2}⌝))
= [⌜e_{1}⌝/x]⌜let val y = e′_{2} in e′′_{2}⌝
= [⌜e_{1}⌝/x]⌜e_{2}⌝.

□

We usually summarize Lemmas 3.2, 3.3, 3.4, and 3.5 into a single adequacy theorem, whose proof is is immediate from the preceding lemmas.

Theorem 3.6 (Adequacy) There is a compositional bijection ⌜·⌝ between Mini-ML expressions with free variables among x_{1}, …, x_{n} and (canonical) LF objects M such that

x_{1}:exp, …, x_{n}:exp ⊢_{E} M ⇑ exp

is derivable. The bijection is compositional in the sense that

⌜[e_{1}/x]e_{2}⌝ = [⌜e_{1}⌝/x]⌜e_{2}⌝.

3.4 Judgments as Types

So far, we have only discussed the representation of the abstract syntax of a language, taking advantage of the expressive power of the simply-typed λ-calculus. The next step will be the representation of deductions. The general approach is to represent deductions as objects and judgments as types. For example, given closed expressions e and v and a deduction

\begin{matrix}D \\ e ↪ v \end{matrix}

we would like to establish that

⊢_{EV} ⌜D⌝ ⇑ ⌜e ↪ v⌝,

where ⌜·⌝ is again a representation function and EV is an LF signature from which the constants in ⌜D⌝ are drawn. That is, the representation of D is a canonical object of type ⌜e ↪ v⌝. The main difficulty will be achieving the converse, namely that if

⊢_{EV} M ⇑ ⌜e ↪ v⌝

then there is a deduction D such that ⌜D⌝ = M.

As a first approximation, assume we declare a type eval of evaluations, similar to the way we declared a type exp of Mini-ML expressions.

eval : type

An axiom would simply be represented as a constant of type eval. An inference rule can be viewed as a constructor which, given deductions of the premisses, yields a deduction of the conclusion. For example, the rules

\frac{ev z}{z ↪ z}  \begin{aligned}e ↪ v \\ & ev s \\ s e ↪ s v \end{aligned}
e_{1} ↪ z e_{2} ↪ v

ev case z

(case e_{1} of z ⇒ e_{2} | s x ⇒ e_{3}) ↪ v

would be represented by

ev z  : eval
ev s  : eval → eval
ev case z : eval → eval → eval.

One can easily see that this representation is not faithful: the declaration of a constant in the signature contains much less information than the statement of the inference rule. For example,

⊢_{EV} ev case z (ev s ev z) ev z ⇑ eval

would be derivable, but the object above does not represent a valid evaluation. The problem is that the first premiss of the rule ev case z must be an evaluation yielding z, while the corresponding argument to ev case z, namely (ev s ev z), represents an evaluation yielding s z.

One solution to this representation problem is to introduce a validity predicate and define when a given object of type eval represents a valid deduction. This is, for example, the solution one would take in a framework such as higher-order Horn clauses or hereditary Harrop formulas. This approach is discussed in a number of papers [MNPS91, NM90, Pau87, Wol91] and also is the basis for the logic programming language λProlog [NM88] and the theorem prover Isabelle [PN90]. Here we take a different approach in that we refine the type system instead in such a way that only the representation of valid deductions (evaluations, in this example) will be well-typed in the meta-language. This has a number of methodological advantages. Perhaps the most important is that checking the validity of a deduction is reduced to a type-checking problem in the logical framework. Since LF typechecking is decidable, this means that checking deductions of the object language is automatically decidable, once a suitable representation in LF has been chosen.

But how do we refine the type system so that the counterexample above is rejected as ill-typed? It is clear that we have to subdivide the type of all evaluations into an infinite number of subtypes: for any expression e and value v there should be a type of deductions of e ↪ v. Of course, many of of these types should be empty. For example, there is no deduction of the judgment s z ↪ z. These considerations lead to the view that eval is a type family indexed by representations of e and v.

Following our representation methodology, both of these will be LF objects of type exp. Thus we have types, such as (eval z z) which depend on objects, a situation which can easily lead to an undecidable type system. In the case of LF we can preserve decidability of type-checking (see Section 3.5). A first approximation to a revision of the representation for evaluations above would be

eval  : exp → exp → type
ev z  : eval z z
ev s  : eval E V → eval (s E) (s V)
ev case z : eval E_{1} z → eval E_{2} V → eval (case E_{1} E_{2} E_{3}) V.

The declarations of ev s and ev case z are schematic in the sense that they are intended to represent all instances with valid objects E, E_{1}, E_{2}, E_{3}, and V of appropriate type. With these declarations the object (ev case z (ev s ev z) ev z) is no longer well-typed, since (ev s ev z) has type eval (s z) (s z), while the first argument to ev case z should have type eval E_{1} z for some E_{1}.

Although it is not apparent in this example, allowing unrestricted schematic declarations would lead to an undecidable type-checking problem for LF, since it would require a form of higher-order unification. Instead we add E_{1}, E_{2}, E_{3}, and V as explicit arguments to ev case z.^{4} A simple function type (formed by →) is not expressive enough to capture the dependencies between the various arguments. For example,

ev case z : exp → exp → (exp → exp) → exp
→ eval E_{1} z → eval E_{2} V → eval (case E_{1} E_{2} E_{3}) V

does not express that the first argument is supposed to be E_{1}, the second argument E_{2}, etc. Thus we must explicitly label the first four arguments: this is what the dependent function type constructor Π achieves. Using dependent function types we write

ev case z : ΠE_{1}:exp. ΠE_{2}:exp. ΠE_{3}:exp → exp. ΠV:exp.
eval E_{1} z → eval E_{2} V → eval (case E_{1} E_{2} E_{3}) V.

Note that the right-hand side is now a closed type since Π binds the variable it quantifies. The function ev case z is now a function of six arguments.

Before continuing the representation, we need to extend the simply-typed framework as presented in Section 3.1 to account for the two new phenomena we have encountered: type families indexed by objects and dependent function types.

4In practice this is often unnecessary and the Elf programming language allows schematic declarations in the form above and performs type reconstruction.

3.5 Adding Dependent Types to the Framework

We now introduce type families and dependent function types into the simply-typed fragment, although at this point not in the full generality of LF.

The first change deals with type families: it is now more complicated to check if a given type is well-formed, since types depend on objects. Moreover, we must be able to declare the type of the indices of type families. This leads to the introduction of kinds, which form another level in the definition of the framework calculus.

Kinds  K ::= type | A → K
Types  A ::= a M_{1} … M_{n} | A_{1} → A_{2} | Πx:A_{1}. A_{2}
Objects  M ::= c | x | λx:A. M | M_{1} M_{2}
Signatures Σ ::= · | Σ, a:K | Σ, c:A
Contexts  Γ ::= · | Γ, x:A

Note that the level of objects has only changed insofar as the types occurring in λ-abstractions may now be more general. Indeed, all functions which can be expressed in this version of the framework could already be expressed in the simplytyped fragment. This highlights our motivation and intuition behind this extension: we refine the type system so that objects that do not represent deductions will be ill-typed. We are not interested in extending the language so that, for example, more functions would be representable.

Type families can be declared via a:K in signatures and instantiated to types as a M_{1} … M_{n}. We refer to such types as atomic types, to types of the form A_{1} → A_{2} as simple function types, and to types of the form Πx:A_{1}. A_{2} as dependent function types. We also need to extend the inference rules for valid types and objects. We now have the basic judgments

Γ ⊢_{Σ} A : type A is a valid type
Γ ⊢_{Σ} M : A M is a valid object of type A

and auxiliary judgments

⊢ Σ Sig  Σ is a valid signature
⊢_{Σ} Γ Ctx  Γ is a valid context
Γ ⊢_{Σ} K Kind K is a valid kind
M ≡ N  M is definitionally equal to N
A ≡ B  A is definitionally equal to B

The judgments are now mutually dependent to a large degree. For example, in order to check that a type is valid, we have to check that the objects occuring in the indices of a type family are valid. The need for the convertibility judgments will be motivated below. Again, there are a variety of possibilities for defining these judgments. The one we give below is perhaps not the most convenient for the metatheory of LF, but it reflects the process of type-checking fairly directly. We begin with the rules defining the valid types.

Σ(a) = A_{1} → ⋯ → A_{n} → type Γ ⊢_{Σ} M_{1} : A_{1} … Γ ⊢_{Σ} M_{n} : A_{n}

atom

Γ ⊢_{Σ} a M_{1} … M_{n} : type

Γ ⊢_{Σ} A : type Γ ⊢_{Σ} B : type

arrow

Γ ⊢_{Σ} A → B : type

Γ ⊢_{Σ} A : type Γ, x:A ⊢_{Σ} B : type

pi

Γ ⊢_{Σ} Πx:A. B : type

The basic rules for valid objects are as before, except that we now have to allow for dependency. The typing rule for applying a function with a dependent type requires some thought. Recall, from the previous section,

ev case z : ΠE_{1}:exp. ΠE_{2}:exp. ΠE_{3}:exp → exp. ΠV:exp.
eval E_{1} z → eval E_{2} V → eval (case E_{1} E_{2} E_{3}) V.

The Π construct was introduced to express the dependency between the first argument and the type of the fifth argument. This means, for example, that we would expect

⊢_{EV} ev case z z z (λx:exp. x) z

: eval z z → eval z z → eval (case z z (λx:exp. x)) z

to be derivable. We have instantiated E_{1} with z, E_{2} with z, E_{3} with (λx:exp. x) and V with z. Thus the typing rule

Γ ⊢_{Σ} M : Πx:A. B Γ ⊢_{Σ} N : A

app

Γ ⊢_{Σ} M N : [N/x]B

emerges. In this rule we can see that the type (and not just the value) of an application of a function M to an argument N may depend on N. This is the reason why Πx:A. B is called a dependent function type. For different reasons it is also sometimes referred to as the dependent product. The rule for λ-abstraction and the other rules do not change significantly.

\begin{aligned}Σ(c) = A \\ & con \\ Γ ⊢_{Σ} c : A \end{aligned}  \begin{aligned}Γ(x) = A \\ & var \\ Γ ⊢_{Σ} x : A \end{aligned}
Γ ⊢_{Σ} A : type Γ, x:A ⊢_{Σ} M : B

lam Γ ⊢_{Σ} λx:A. M : Πx:A. B

The prior rules for functions of simple type are still valid, with the restriction that x may not occur free in B in the rule lam′′. This restriction is necessary, since it is now possible for x to occur in B because objects (including variables) can appear inside types.

Γ ⊢_{Σ} A : type Γ, x:A ⊢_{Σ} M : B

lam′′ Γ ⊢_{Σ} λx:A. M : A → B

Γ ⊢_{Σ} M : A → B Γ ⊢_{Σ} N : A app′′

Γ ⊢_{Σ} M N : B

The type system as given so far has a certain redundancy and is also no longer syntax-directed. That is, there are two rules for λ-abstraction (lam and lam′′) and application. It is convenient to eliminate this redundancy by allowing A → B as a notation for Πx:A. B whenever x does not occur in B. It is easy to see that under this convention, the rules lam′′ and app′′ are valid rules of inference, but are no longer necessary since any of their instances are also instances of lam and app.

The rules for valid signatures, contexts, and kinds are straightforward and left as Exercise 3.10. They are a special case of the rules for full LF given in Section 3.8.

One rule which is still missing is the rule of type conversion. Type conversion introduces a major complication into the type system and is difficult to motivate and illustrate with the example as we have developed it so far. We take a brief excursion and introduce another example to illustrate the necessity for the type conversion rule. Consider a potential application of dependent types in functional programming, where we would like to index the type of vectors of integers by the length of the vector. That is, vector is a type family, indexed by integers.

int  : type
plus : int → int → int
vector : int → type

Furthermore, assume we can assign the following type to the function which concatenates two vectors:

concat : Πn:int. Πm:int. vector n → vector m → vector (plus n m).

Then we would obtain the typings

concat 3 2 ⟨1, 2, 3⟩ ⟨4, 5⟩ : vector (plus 3 2)
⟨1, 2, 3, 4, 5⟩ : vector 5.

But since the first expression presumably evaluates to the second, we would expect ⟨1, 2, 3, 4, 5⟩ to have type vector (plus 3 2), or the first expression to have type vector 5—otherwise the language would not preserve types under evaluation.

This example illustrates two points. The first is that adding dependent types to functional languages almost invariably leads to an undecidable type-checking problem, since with the approach above one could easily encode static array bounds checking. The second is that we need to allow conversion between equivalent types. In the example above, vector (plus 3 2) ≡ vector 5. Thus we need a notion of definitional equality and add the rule of type conversion to the system we have considered so far.

Γ ⊢_{Σ} M : A  A ≡ B  Γ ⊢_{Σ} B : type

conv

Γ ⊢_{Σ} M : B

It is necessary to check the validity of B in the premiss, since we have followed the standard technique of formulating definitional equality as an untyped judgment, and a valid type may be convertible to an invalid type. As hinted earlier, the notion of definitional equality that is most useful for our purposes is based on β- and ηconversion. We postpone the full definition until the need for these conversions is better motivated from the example.

3.6 Representing Evaluations

We summarize the signature for evaluations as we have developed it so far, taking advantage of type families and dependent types.

eval  : exp → exp → type
ev z  : eval z z
ev s  : ΠE:exp. ΠV:exp. eval E V → eval (s E) (s V)
ev case z : ΠE_{1}:exp. ΠE_{2}:exp. ΠE_{3}:exp → exp. ΠV:exp.
eval E_{1} z → eval E_{2} V → eval (case E_{1} E_{2} E_{3}) V

The representation function on derivations using these rules is defined inductively on the structure of the derivation.

⌜ \frac{ev z}{z ↪ z} ⌝ = ev z

⌜ \begin{matrix}D \\ e ↪ v \end{matrix} ; ev s ; s e ↪ s v ⌝ = ev s ⌜e⌝ ⌜v⌝ ⌜D⌝

⌜

\begin{matrix}D_{1} \\ e_{1} ↪ z \end{matrix}  \begin{matrix}D_{2} \\ e_{2} ↪ v \end{matrix}

ev case z

(case e_{1} of z ⇒ e_{2} | s x ⇒ e_{3}) ↪ v

⌝

= ev case z ⌜e_{1}⌝ ⌜e_{2}⌝ (λx:exp. ⌜e_{3}⌝) ⌜v⌝ ⌜D_{1}⌝ ⌜D_{2}⌝

The rules dealing with pairs are straightforward and introduce no new representation techniques. We leave them as Exercise 3.4. Next we consider the rule for evaluating a Mini-ML expression formed with lam. For this rule we will examine more closely why, for example, E_{3} in the ev case z rule was assumed to be of type exp → exp.

\frac{ev lam}{lam x. e ↪ lam x. e}

Recall that the representation function employs the idea of higher-order abstract syntax:

⌜lam x. e⌝ = lam (λx:exp. ⌜e⌝).

An incorrect attempt at a direct representation of the inference rule above would be

ev lam : ΠE:exp. eval (lam (λx:exp. E)) (lam (λx:exp. E)).

The problem with this formulation is that, because of the variable naming hygiene of the framework, we cannot instantiate E with an object which contains x free. That is, for example,

\frac{ev lam}{lam x. x ↪ lam x. x}

could not be represented by (ev lam x) since its type would be

[x/E]eval (lam (λx:exp. E)) (lam (λx:exp. E))
= eval (lam (λx′:exp. x)) (lam (λx′:exp. x))
≠ eval (lam (λx:exp. x)) (lam (λx:exp. x))
= eval ⌜lam x. x⌝ ⌜lam x. x⌝

for some new variable x′. Instead, we have to bundle the scope of the bound variable with its binder into a function from exp to exp, the type of the argument to lam.

ev lam : ΠE:exp → exp. eval (lam E) (lam E).

Now the evaluation of the identity function above would be correctly represented by (ev lam (λx:exp. x)) which has type

[(λx:exp. x)/E]eval (lam E) (lam E)
= eval (lam (λx:exp. x)) (lam (λx:exp. x)).

To summarize this case, we have

⌜ \frac{ev lam}{lam x. e ↪ lam x. e} ⌝ = ev lam (λx:exp. ⌜e⌝).

Yet another new technique is introduced in the representation of the rule which deals with applying a function formed by lam to an argument.

e_{1} ↪ lam x. e′_{1}  e_{2} ↪ v_{2}  [v_{2}/x]e′_{1} ↪ v ev app
e_{1} e_{2} ↪ v

As in the previous example, e′_{1} must be represented with its binder as a function from exp to exp. But how do we represent [v_{2}/x]e′_{1}? The substitution lemma (Lemma 3.5) tell us that

⌜[v_{2}/x]e′_{1}⌝ = [⌜v_{2}⌝/x]⌜e′_{1}⌝.

The right-hand side is β-convertible to (λx:exp. ⌜e′_{1}⌝) ⌜v_{2}⌝. Note that the function part of this application, (λx:exp. ⌜e′_{1}⌝) will be an argument to the constant representing the rule, and we can thus directly apply it to the argument representing v_{2}. These considerations lead to the declaration

ev app : ΠE_{1}:exp. ΠE_{2}:exp. ΠE′_{1}:exp → exp. ΠV_{2}:exp. ΠV:exp.
eval E_{1} (lam E′_{1})
→ eval E_{2} V_{2}
→ eval (E′_{1} V_{2}) V
→ eval (app E_{1} E_{2}) V

where ⌜

\begin{matrix}D_{1} \\ e_{1} ↪ lam x. e′_{1} \end{matrix}  \begin{matrix}D_{2} \\ e_{2} ↪ v_{2} \end{matrix}  \begin{matrix}D_{3} \\ [v_{2}/x]e′_{1} ↪ v \end{matrix}

ev app

e_{1} e_{2} ↪ v

⌝

= ev app ⌜e_{1}⌝ ⌜e_{2}⌝ (λx:exp. ⌜e′_{1}⌝) ⌜v_{2}⌝ ⌜v⌝ ⌜D_{1}⌝ ⌜D_{2}⌝ ⌜D_{3}⌝.

Consider the evaluation of the Mini-ML expression (lam x. x) z:

\frac{ev lam}{lam x. x ↪ lam x. x}  \frac{ev z}{z ↪ z}  \frac{ev z}{z ↪ z}

ev app

(lam x. x) z ↪ z

Note that the third premiss is a deduction of [z/x]x ↪ z which is z ↪ z. The whole deduction is represented by the LF object

ev app (lam (λx:exp. x)) z (λx:exp. x) z z (ev lam (λx:exp. x)) ev z ev z.

But why is this well-typed? The crucial question arises with the last argument to ev app. By substitution into the type of ev app we find that the last argument is required to have type (eval ((λx:exp. x) z) z), while the actual argument, ev z, has type eval z z. The rule of type conversion allows us to move from one type to the other provided they are definitionally equal. Thus our notion of definitional equality must include β-conversion in order to allow the representation technique whereby object-level substitution is represented by meta-level β-reduction.

In the seminal paper on LF [HHP93], definitional equality was based only on β-reduction, due to technical problems in proving the decidability of the system including η-conversion. The disadvantage of the system with only β-reduction is that not every object is convertible to a canonical form using only β-conversion (see the counterexample on page 44). This property holds once η-conversion is added. The decidability of the system with both βη-conversion has since been proven using three different techniques [Sal92, Coq91, Geu92].

The remaining rule of the operational semantics of Mini-ML follow the pattern of the previous rules.

e_{1} ↪ s v′_{1} [v′_{1}/x]e_{3} ↪ v

ev case s

(case e_{1} of z ⇒ e_{2} | s x ⇒ e_{3}) ↪ v

ev case s : ΠE_{1}:exp. ΠE_{2}:exp. ΠE_{3}:exp → exp. ΠV′_{1}:exp. ΠV:exp.

eval E_{1} (s V′_{1}) → eval (E_{3} V′_{1}) V → eval (case E_{1} E_{2} E_{3}) V

e_{1} ↪ v_{1} [v_{1}/x]e_{2} ↪ v

ev letv

let val x = e_{1} in e_{2} ↪ v

ev letv : ΠE_{1}:exp. ΠE_{2}:exp → exp. ΠV_{1}:exp. ΠV:exp.

eval E_{1} V_{1} → eval (E_{2} V_{1}) V → eval (letv E_{1} E_{2}) V

[e_{1}/x]e_{2} ↪ v

ev letn

let name x = e_{1} in e_{2}

ev letn : ΠE_{1}:exp. ΠE_{2}:exp → exp. ΠV:exp.
eval (E_{2} E_{1}) V → eval (letn E_{1} E_{2}) V

For the fixpoint construct, we have to substitute a compound expression and not just a variable.

[fix x. e/x]e ↪ v

ev fix

fix x. e ↪ v

ev fix : ΠE:exp → exp. ΠV:exp.

eval (E (fix E)) V → eval (fix E) V

Again we are taking advantage of the substitution lemma in the form

⌜[fix x. e/x]e⌝ = [⌜fix x. e⌝/x]⌜e⌝ ≡ (λx:exp. ⌜e⌝) ⌜fix x. e⌝.

The succession of representation theorems follows the pattern of Section 3.3. Note that we postulate that e and v be closed, that is, do not contain any free variables. We state this explicitly, because according to the earlier inference rules, there is no requirement that lam x. e be closed in the ev lam rule. However, we would like to restrict attention to closed expressions e, since they are the only ones which will be well-typed in the empty context within the Mini-ML typing discipline. The generalization of the canonical form judgment to LF in the presence of dependent types is given in Section 3.9.

Lemma 3.7 Let e and v be closed Mini-ML expressions, and D a derivation of e ↪ v. Then

⊢_{EV} ⌜D⌝ ⇑ eval ⌜e⌝ ⌜v⌝.

Proof: The proof proceeds by induction on the structure of D. We show only one case—the others are similar and simpler.

Case: D = \begin{matrix}D_{1} \\ e_{1} ↪ lam x. e′_{1} \end{matrix}  \begin{matrix}D_{2} \\ e_{2} ↪ v_{2} \end{matrix}  \begin{matrix}D_{3} \\ [v_{2}/x]e′_{1} ↪ v \end{matrix} ev app e_{1} e_{2} ↪ v. Then
⌜D⌝ = ev app ⌜e_{1}⌝ ⌜e_{2}⌝ (λx:exp. ⌜e′_{1}⌝) ⌜v_{2}⌝ ⌜v⌝ ⌜D_{1}⌝ ⌜D_{2}⌝ ⌜D_{3}⌝

By the adequacy of the representation of expressions (Theorem 3.6), ⌜e_{1}⌝, ⌜e_{2}⌝, ⌜v_{2}⌝, and ⌜v⌝ are canonical of type exp. Furthermore, ⌜e′_{1}⌝ is canonical of type exp and one application of the carrow rule yields

⊢_{EV} ⌜e′_{1}⌝ ⇑ exp carrow,

⊢_{EV} λx:exp. ⌜e′_{1}⌝ ⇑ exp → exp

that is, λx:exp. ⌜e′_{1}⌝ is canonical of type exp → exp.

By the induction hypothesis on D_{1}, we have

⊢_{EV} D_{1} ⇑ eval ⌜e_{1}⌝ ⌜lam x. e′_{1}⌝

and hence by the definition of the representation function

⊢_{EV} D_{1} ⇑ eval ⌜e_{1}⌝ (lam (λx:exp. ⌜e′_{1}⌝))

Furthermore, by induction hypothesis on D_{2},

⊢_{EV} D_{2} ⇑ eval ⌜e_{2}⌝ ⌜v_{2}⌝.

Recalling the declaration of ev app,

ev app : ΠE_{1}:exp. ΠE_{2}:exp. ΠE′_{1}:exp → exp. ΠV_{2}:exp. ΠV:exp.
eval E_{1} (lam E′_{1})
→ eval E_{2} V_{2}
→ eval (E′_{1} V_{2}) V
→ eval (app E_{1} E_{2}) V,

we conclude that

ev app ⌜e_{1}⌝ ⌜e_{2}⌝ (λx:exp. ⌜e′_{1}⌝) ⌜v_{2}⌝ ⌜v⌝ ⌜D_{1}⌝ ⌜D_{2}⌝
: eval ((λx:exp. ⌜e′_{1}⌝) ⌜v_{2}⌝) ⌜v⌝ → eval (app ⌜e_{1}⌝ ⌜e_{2}⌝) ⌜v⌝.

The type here is not in canonical form, since (λx:exp. ⌜e′_{1}⌝) is applied to ⌜v_{2}⌝. With the rule of type conversion we now obtain

ev app ⌜e_{1}⌝ ⌜e_{2}⌝ (λx:exp. ⌜e′_{1}⌝) ⌜v_{2}⌝ ⌜v⌝ ⌜D_{1}⌝ ⌜D_{2}⌝
: eval ([⌜v_{2}⌝/x]⌜e′_{1}⌝) ⌜v⌝ → eval (app ⌜e_{1}⌝ ⌜e_{2}⌝) ⌜v⌝.

where [⌜v_{2}⌝/x]⌜e′_{1}⌝ is a valid object of type exp. The application of the object above to ⌜D_{3}⌝ (which yields ⌜D⌝) can be seen as type-correct, since the induction hypothesis on D_{3} yields

⊢_{EV} D_{3} ⇑ eval ⌜[v_{2}/x]e′_{1}⌝ ⌜v⌝,

and from the substitution lemma (Lemma 3.5) we know that

⌜[v_{2}/x]e′_{1}⌝ = [⌜v_{2}⌝/x]⌜e′_{1}⌝.

Furthermore, D is canonical, since it is atomic and all the arguments to ev app are in canonical form.

□

Lemma 3.8 For any LF objects E, V, and M such that ⊢_{EV} E ⇑ exp, ⊢_{EV} V ⇑ exp and ⊢_{EV} M ⇑ eval E V, there exist unique Mini-ML expressions e and v and a deduction D :: e ↪ v such that ⌜e⌝ = E, ⌜v⌝ = V and ⌜D⌝ = M.

Proof: The proof is by structural induction on the derivation of ⊢_{EV} M ⇑ eval E V (see Exercise 3.12). □

A substitution lemma does not arise in the same way as it arose for expressions since evaluations are closed. However, as we know from the use of Lemma 3.5 in the proof of type preservation (Theorem 2.5), a substitution lemma for Mini-ML typing derivations plays an important role. We will return to this in Section 5.4. As before, we summarize the correctness of the representation into an adequacy theorem. It follows directly from Lemmas 3.7 and 3.8.

Theorem 3.9 (Adequacy) There is a bijection between deductions of e ↪ v for closed Mini-ML expressions e and v and canonical LF objects M such that

⊢_{EV} M ⇑ eval ⌜e⌝ ⌜v⌝

As a second example for the representation of deductions we consider the judgment e Value, defined in Section 2.4. Again, the judgment is represented as a type family, value, indexed by the representation of the expression e. That is,

value : exp → type

Objects of type value ⌜e⌝ then represent deductions, and inference rules are encoded as constructors for objects of such types.

val z  : value z
val s  : ΠE:exp. value E → value (s E)
val pair : ΠE_{1}:exp. ΠE_{2}:exp. value E_{1} → value E_{2} → value (pair E_{1} E_{2})
val lam : ΠE:exp → exp. value (lam E)

In the last rule, the scope of the binder lam is represented as a function from expressions to expressions. We refer to the signature above (including the signature E representing Mini-ML expressions) as V. We omit the obvious definition of the representation function on value deductions. The representation theorem only refers to its existence implicitly.

Theorem 3.10 (Adequacy) For closed expressions e there is a bijection between deductions P :: e Value and canonical LF objects M such that ⊢_{V} M ⇑ value ⌜e⌝ is derivable.

Proof: See Exercise 3.13. □

3.7 Meta-Theory via Higher-Level Judgments

So far we have completed two of the tasks we set out to accomplish in this chapter: the representation of abstract syntax and the representation of deductive systems in a logical framework. This corresponds to the specification of a language and its semantics. The third task now before us is the representation of the meta-theory of the language, that is, proofs of properties of the language and its semantics.

This representation of meta-theory should naturally fit within the framework we have laid out so far. It should furthermore reflect the structure of the informal proof as directly as possible. We are thus looking for a formal language and methodology for expressing a given proof, and not for a system or environment for finding such a proof. Once such a methodology has been developed it can also be helpful in proof search, but we would like to emphasize that this is a secondary consideration. In order to design a proof representation we must take stock of the proof techniques we have seen so far. By far the most pervasive is structural induction. Structural induction is applied in various forms: we have used induction over the structure of expressions, and induction over the structure of deductions. Within proofs of the latter kind we have also frequent cause to appeal to inversion, that is, from the form of a derivable judgment we make statements about which inference rule must have been applied to infer it. Of course, as is typical in mathematics, we break down a proof into a succession of lemmas leading up to a main theorem. A kind of lemma which arises frequently when dealing with deductive systems is a substitution lemma.

We first consider the issue of structural induction and its representation in the framework. At first glance, this seems to require support for logical reasoning, that is, we need quantifiers and logical connectives to express a meta-theorem, and logical axioms and inference rules to prove it. Our framework does not support this directly—we would either have to extend it very significantly or encode the logic we are attempting to model just like any other deductive system. Both of these approaches have some problems. The first does not mesh well with the idea of higher-order abstract syntax, basically because the types (such as the type exp of Mini-ML expressions) are not inductively defined in the usual sense.^{5} Similar problems arise when encoding deductive systems employing parametric and hypothetical judgments such as the Mini-ML typing judgment. The second approach, that is, to first define a logical system and then reason within it, incurs a tremendous overhead in additional machinery to be developed. Furthermore, the connection between the direct representations given in the previous sections of this chapter and this indirect method is problematic.

5[The problem arises from the negative occurrences of exp in the type of case, lam, let, and fix.]

Thus we are looking for a more direct way to exploit the expressive power of the framework we have developed so far. We will use Theorem 2.1 (value soundness for Mini-ML) and its proof as a motivating example. Recall that the theorem states that whenever e ↪ v is derivable, then v Value is also derivable. The proof proceeds by an induction on the structure of the derivation of e ↪ v.

A first useful observation is that the proof is constructive in the sense that it implicitly contains a method for constructing a deduction P of the judgment v Value, given a deduction D of e ↪ v. This is an example of the relationship between constructive proofs and programs considered further in Sections 7.4 through 7.6. Could we exploit the converse, that is, in what sense might the function f for constructing P from D represent a proof of the theorem? Such a function f, if it were expressible in the framework, would presumably have type ΠE:exp. ΠV:exp. eval E V → value V. If it were guaranteed that a total function of this type existed, our meta-theorem would be verified. Unfortunately, such a function is not realizable within the logical framework, since it would have to be defined by a form of recursion on an object of type eval E V. Attempting to extend the framework in a straightforward way to encompass such function definitions invalidates our approach to abstract syntax and hypothetical judgments.

But we have one further possibility: why not represent the connection between D :: e ↪ v and P :: v Value as a judgment (defined by inference rules) rather than a function? This technique is well-known from logic programming, where predicates (defined via Horn clauses) rather than functions give rise to computation. A related operational interpretation for LF signatures (which properly generalize sets of Horn clauses) forms the basis for the Elf programming language discussed in Chapter 4. To restate the idea: we represent the essence of the proof of value soundness as a judgment relating deductions D :: e ↪ v and P :: v Value. Judgments relating deductions are not uncommon in the meta-theory of logic. An important example is the judgment that a natural deduction reduces to another natural deduction, which we will discuss in Section 7.1.

In order to illustrate this approach, we quote various cases in the proof of value soundness and try to extract the inference rules for the judgment we motivated above. We write the judgment as

\begin{matrix}D \\ e ↪ v \end{matrix} ⟹ \begin{matrix}P \\ v Value \end{matrix}

and read it as “D reduces to P.” Following this analysis, we give its representation in LF. Recall that the proof is by induction over the structure of the deduction D :: e ↪ v.

Case: D = \frac{ev z.}{z ↪ z} Then v = z is a value by the rule val z.

This gives rise to the axiom

vs z

\frac{ev z}{z ↪ z} ⟹ \frac{val z}{z Value}

Case:

D = \begin{matrix}D_{1} \\ e_{1} ↪ v_{1} \end{matrix} ; ev s. ; s e_{1} ↪ s v_{1}

The induction hypothesis on D_{1} yields a deduction of v_{1} Value. Using the inference rule val s we conclude that s v_{1} Value.

This case in the proof is represented by the following inference rule.

\begin{matrix}D_{1} \\ e_{1} ↪ v_{1} \end{matrix} ⟹ \begin{matrix}P_{1} \\ v_{1} Value \end{matrix}

vs s

\begin{matrix}D_{1} \\ e_{1} ↪ v_{1} \end{matrix} ; ev s ; s e_{1} ↪ s v_{1} ⟹ \begin{matrix}P_{1} \\ v_{1} Value \end{matrix} ; val s ; s v_{1} Value

Here, the appeal to the induction hypothesis on D_{1} has been represented in the premiss, where we have to establish that D_{1} reduces to P_{1}.

Case:

D = \begin{matrix}D_{1} \\ e_{1} ↪ z \end{matrix}  \begin{matrix}D_{2} \\ e_{2} ↪ v \end{matrix} ev case z. (case e_{1} of z ⇒ e_{2} | s x ⇒ e_{3}) ↪ v

Then the induction hypothesis applied to D_{2} yields a deduction of v Value, which is what we needed to show in this case.

In this case, the appeal to the induction hypothesis immediately yields the correct deduction; no further inference is necessary.

\begin{matrix}D_{2} \\ e_{2} ↪ v \end{matrix} ⟹ \begin{matrix}P_{2} \\ v Value \end{matrix}

vs case z

\begin{matrix}D_{1} \\ e_{1} ↪ z \end{matrix} \begin{matrix}D_{2} \\ e_{2} ↪ v \end{matrix} ev case z (case e_{1} of z ⇒ e_{2} | s x ⇒ e_{3}) ↪ v ⟹ \begin{matrix}P_{2} \\ v Value \end{matrix}

Case:

D = \begin{matrix}D_{1} \\ e_{1} ↪ s v′_{1} \end{matrix} \begin{matrix}D_{3} \\ [v′_{1}/x]e_{3} ↪ v \end{matrix} ev case s. (case e_{1} of z ⇒ e_{2} | s x ⇒ e_{3}) ↪ v

Then the induction hypothesis applied to D_{3} yields a deduction of v Value, which is what we needed to show in this case.

This is like the previous case.

\begin{matrix}D_{3} \\ [v′_{1}/x]e_{3} ↪ v \end{matrix} ⟹ \begin{matrix}P_{3} \\ v Value \end{matrix}

vs case s

\begin{matrix}D_{1} \\ e_{1} ↪ s v′_{1} \end{matrix} \begin{matrix}D_{3} \\ [v′_{1}/x]e_{3} ↪ v \end{matrix} ev case s (case e_{1} of z ⇒ e_{2} | s x ⇒ e_{3}) ↪ v ⟹ \begin{matrix}P_{3} \\ v Value \end{matrix}

If D ends in ev pair we reason similar to cases above.

Case:

D = \begin{matrix}D′ \\ e ↪ ⟨v_{1}, v_{2}⟩ \end{matrix} ev fst. fst e ↪ v_{1}

Then the induction hypothesis applied to D′ yields a deduction P′ of the judgment ⟨v_{1}, v_{2}⟩ Value. By examining the inference rules we can see that P′ must end in an application of the val pair rule, that is,

P′ = \begin{matrix}P_{1} \\ v_{1} Value \end{matrix} \begin{matrix}P_{2} \\ v_{2} Value \end{matrix} val pair ⟨v_{1}, v_{2}⟩ Value

for some P_{1} and P_{2}. Hence v_{1} Value must be derivable, which is what we needed to show.

In this case we also have to deal with an application of inversion in the informal proof, analyzing the possible inference rules in the last step of the derivation P′ :: ⟨v_{1}, v_{2}⟩ Value. The only possibility is val pair. In the representation of this case as an inference rule for the ⟹ judgment, we require that the right-hand side of the premiss end in this inference rule.

\begin{matrix}D′ \\ e ↪ ⟨v_{1}, v_{2}⟩ \end{matrix} ⟹ \begin{matrix}P_{1} \\ v_{1} Value \end{matrix} \begin{matrix}P_{2} \\ v_{2} Value \end{matrix} val pair ⟨v_{1}, v_{2}⟩ Value

vs fst

\begin{matrix}D′ \\ e ↪ ⟨v_{1}, v_{2}⟩ \end{matrix} ev fst fst e ↪ v_{1} ⟹ \begin{matrix}P_{1} \\ v_{1} Value \end{matrix}

The remaining cases are similar to the ones shown above and left as an exercise (see Exercise 3.8). While our representation technique should be clear from the example, it also appears to be extremely unwieldy. The explicit definition of the ⟹ judgment given above is fortunately only a crutch in order to explain the LF signature which follows below. In practice we do not make this intermediate form explicit, but directly express the proof of a meta-theorem as an LF signature. Such signatures may seem very cumbersome, but the type reconstruction phase of the Elf implementation allows very concise signature specifications that are internally expanded into the form shown below.

The representation techniques given so far suggest that we represent the judgment

\begin{matrix}D \\ e ↪ v \end{matrix} ⟹ \begin{matrix}P \\ v Value \end{matrix}

as a type family indexed by the representation of the deductions D and P, that is,

vs : eval E V → value V → type

Once again we need to resolve the status of the free variables E and V in order to achieve (in general) a decidable type reconstruction problem. Before, we used the dependent function type constructor Π to turn them into explicit arguments to object level constants. Here, we need to index the type family vs explicitly by E and V, both of type exp. Thus we need to extend the language for kinds (which classify type families) to admit dependencies and allow the declaration

vs : ΠE:exp. ΠV:exp. eval E V → value V → type.

The necessary generalization of the system from Section 3.5 is given in Section 3.8. The main change is a refinement of the language for kinds by admitting dependencies, quite analogous to the previous refinement of the language of types when we generalized the simply-typed fragment of Section 3.1.

We now consider the representation of some of the rules of the judgment ⟹ as LF objects. The axiom

vs z

\frac{ev z}{z ↪ z} ⟹ \frac{val z}{z Value}

is represented as

vs z : vs z z ev z val z.

The instantiation of the type family vs is valid, since ev z : eval z z and val z : value z.

The second rule we considered arose from the case where the evaluation ended in the rule for successor.

\begin{matrix}D_{1} \\ e_{1} ↪ v_{1} \end{matrix} ⟹ \begin{matrix}P_{1} \\ v_{1} Value \end{matrix}

vs s

\begin{matrix}D_{1} \\ e_{1} ↪ v_{1} \end{matrix} ; ev s ; s e_{1} ↪ s v_{1} ⟹ \begin{matrix}P_{1} \\ v_{1} Value \end{matrix} ; val s ; s v_{1} Value

Recall the declarations for ev s and val s.

ev s : ΠE:exp. ΠV:exp. eval E V → eval (s E) (s V)
val s : ΠE:exp. value E → value (s E)

The declaration corresponding to vs s:

vs s : ΠE_{1}:exp. ΠV_{1}:exp.
ΠD_{1}:eval E_{1} V_{1}. ΠP_{1}:value V_{1}.

vs E_{1} V_{1} D_{1} P_{1} → vs (s E_{1}) (s V_{1}) (ev s E_{1} V_{1} D_{1}) (val s V_{1} P_{1}).

We consider one final example, where inversion was employed in the informal proof.

\begin{matrix}D′ \\ e ↪ ⟨v_{1}, v_{2}⟩ \end{matrix} ⟹ \begin{matrix}P_{1} \\ v_{1} Value \end{matrix} \begin{matrix}P_{2} \\ v_{2} Value \end{matrix} val pair ⟨v_{1}, v_{2}⟩ Value

vs fst

\begin{matrix}D′ \\ e ↪ ⟨v_{1}, v_{2}⟩ \end{matrix} ev fst fst e ↪ v_{1} ⟹ \begin{matrix}P_{1} \\ v_{1} Value \end{matrix}

We recall the types for the inference rule encodings involved here:

val pair : ΠE:exp. ΠE_{2}:exp. value E_{1} → value E_{2} → value (pair E_{1} E_{2})
ev fst : ΠE:exp. ΠV_{1}:exp. ΠV_{2}:exp.
eval E (pair V_{1} V_{2}) → eval (fst E) V_{1}

The rule above can then be represented as

vs fst : ΠE_{1}:exp. ΠV_{1}:exp. ΠV_{2}:exp.
ΠD′:eval E (pair V_{1} V_{2}). ΠP_{1}:value V_{1}. ΠP_{2}:value V_{2}.
vs E (pair V_{1} V_{2}) D′ (val pair V_{1} V_{2} P_{1} P_{2})
→ vs (fst E) V_{1} (ev fst E V_{1} V_{2} D′) P_{1}

What have we achieved with this representation of the proof of value soundness in LF? The first observation is the obvious one, namely a representation theorem relating this signature to the ⟹ judgment. Let P be the signature containing the declaration for expressions, evaluations, value deductions, and the declarations above encoding the ⟹ judgment via the type family vs.

Theorem 3.11 (Adequacy) For closed expressions e and v, there is a compositional bijection between deductions of

\begin{matrix}D \\ e ↪ v \end{matrix} ⟹ \begin{matrix}P \\ v Value \end{matrix}

and canonical LF objects M such that

⊢_{P} M ⇑ vs ⌜e⌝ ⌜v⌝ ⌜D⌝ ⌜P⌝

is derivable.

This representation theorem is somewhat unsatisfactory, since the connection between the informal proof of value soundness and the LF signature remains unstated and unproven. It is difficult to make this relationship precise, since the informal proof is not given as a mathematical object. But we can claim and prove a stronger version of the value soundness theorem in which this connection is more explicit.

Theorem 3.12 (Explicit Value Soundness) For any two expressions e and v and deduction D :: e ↪ v there exists a deduction P :: v Value such that

\begin{matrix}D \\ e ↪ v \end{matrix} ⟹ \begin{matrix}P \\ v Value \end{matrix}

is derivable.

Proof: By a straightfoward induction on the structure of D :: e ↪ v (see Exercise 3.14). □

Coupled with the proofs of the various representation theorems for expressions and deductions this establishes a formal connection between value soundness and the vs type family. Yet the essence of the relationship between the informal proof and its representation in LF lies in the connection between the informal proof and its representation of the ⟹ judgment, and this remains implicit. To appreciate this problem, consider the judgment

\begin{matrix}D \\ e ↪ v \end{matrix} ⟹^{triv} \begin{matrix}P \\ v Value \end{matrix}

which is defined via a single axiom

\frac{vs triv.}{\begin{matrix}D \\ e ↪ v \end{matrix} ⟹^{triv} \begin{matrix}P \\ v Value \end{matrix}}

By value soundness and the uniqueness of the deduction of v Value for a given v, the ⟹ and ⟹^{triv} judgments have the same extent, that is, D ⟹ P is derivable iff D ⟹^{triv} P is derivable, but one would hardly claim that ⟹^{triv} represents some informal proof of value soundness.

Ideally, we would like to establish some decidable, formal notion similar to the validity of LF objects which would let us check that the type family vs indeed represents some proof of value soundness. Such a notion can be given in the form of schema-checking which guarantees that a type family such as vs inductively defines a total function from its first three arguments to its fourth argument. A discussion of schema-checking [Roh96] is beyond the scope of these notes. Some material may also be found in the documentation which accompanies the implementation of Elf.^{6}

6[some further remarks later in the notes?]

3.8 The Full LF Type Theory

The levels of kinds and types in the system from Section 3.5 were given as

Kinds K ::= type | A → K
Types A ::= a M_{1} … M_{n} | A_{1} → A_{2} | Πx:A_{1}. A_{2}

We now make two changes: the first is a generalization in that we allow dependent kinds Πx:A. K. The kind of the form A → K is then a special case of the new construct where x does not occur in K. The second change is to eliminate the multiple argument instantiation of type families. This means we generalize to a level of families, among which we distinguish the types as families of kind “type.”

Kinds  K ::= type | Πx:A. K
Families  A ::= a | A M | Πx:A_{1}. A_{2}
Objects  M ::= c | x | λx:A. M | M_{1} M_{2}
Signatures Σ ::= · | Σ, a:K | Σ, c:A
Contexts  Γ ::= · | Γ, x:A

This system differs only minimally from the one given by Harper, Honsell, and Plotkin in [HHP93]. They also allow families to be formed by explicit abstraction, that is, λx:A_{1}. A_{2} is a legal family. These do not occur in normal forms and we have thus chosen to omit them from our system. As mentioned previously, it also differs in that we allow β and η-conversion between objects as the basis for our notion of definitional equality, while in [HHP93] only β-conversion is considered. The judgments take a slightly different form than in Section 3.5, in that we now need to introduce a judgment to explicitly classify families.

Γ ⊢_{Σ} A : K A is a valid family of kind K
Γ ⊢_{Σ} M : A M is a valid object of type A
Γ ⊢_{Σ} K Kind K is a valid kind
⊢ Σ Sig  Σ is a valid signature
⊢_{Σ} Γ Ctx  Γ is a valid context
M ≡ N  M is definitionally equal to N
A ≡ B  A is definitionally equal to B
K ≡ K′  K is definitionally equal to K′

These judgments are defined via the following inference rules.

Σ(a) = K ; famcon ; Γ ⊢_{Σ} a : K

Γ ⊢_{Σ} A : Πx:B. K Γ ⊢_{Σ} M : B

famapp

Γ ⊢_{Σ} A M : [M/x]K

Γ ⊢_{Σ} A : type Γ, x:A ⊢_{Σ} B : type

fampi

Γ ⊢_{Σ} Πx:A. B : type

Σ(c) = A ; objcon ; Γ ⊢_{Σ} c : A Γ(x) = A ; objvar ; Γ ⊢_{Σ} x : A
Γ ⊢_{Σ} A : type Γ, x:A ⊢_{Σ} M : B

objlam

Γ ⊢_{Σ} λx:A. M : Πx:A. B

Γ ⊢_{Σ} M : Πx:A. B Γ ⊢_{Σ} N : A

objapp

Γ ⊢_{Σ} M N : [N/x]B

Γ ⊢_{Σ} M : A  A ≡ B  Γ ⊢_{Σ} B : type

typcnv

Γ ⊢_{Σ} M : B

\frac{kndtyp}{Γ ⊢_{Σ} type Kind}
Γ ⊢_{Σ} A : type Γ, x:A ⊢_{Σ} K Kind

kndpi

Γ ⊢_{Σ} Πx:A. K Kind

Γ ⊢_{Σ} A : K  K ≡ K′  Γ ⊢_{Σ} K′ Kind

kndcnv

Γ ⊢_{Σ} A : K′

\frac{ctxemp}{⊢_{Σ} · Ctx}

Γ ⊢_{Σ} A : type ⊢_{Σ} Γ Ctx

ctxobj

⊢_{Σ} Γ, x:A Ctx

\frac{sigemp}{⊢ · Sig}
⊢_{Σ} K Kind ⊢ Σ Sig

sigfam

⊢ Σ, a : K valid

⊢_{Σ} A : type ⊢ Σ Sig

sigobj

⊢ Σ, c : A Sig

For definitional equality, we have three classes of rules. The first class of rules introduces the conversions.

\frac{beta}{(λx:A. M) N ≡ [N/x]M} ; \frac{eta^{∗}}{(λx:A. M x) ≡ M}

where η is restricted to the case the x is not free in M. The second class of rules specifies that ≡ is an equivalence, satisfying reflexivity, symmetry, and transitivity at each level.

\frac{kndrefl}{K ≡ K} \begin{aligned}K′ ≡ K \\ & kndsym \\ K ≡ K′ \end{aligned} K ≡ K′′ K′′ ≡ K′ kndtrans K ≡ K′

\frac{famrefl}{A ≡ A}

\begin{aligned}B ≡ A \\ & famsym \\ A ≡ B \end{aligned}

A ≡ C C ≡ B

famtrans

A ≡ B

\frac{objrefl}{M ≡ M}

\begin{aligned}N ≡ M \\ & objsym \\ M ≡ N \end{aligned}

M ≡ O O ≡ N

objtrans

M ≡ N

Finally we require rules to ensure that ≡ is a congruence, that is, conversion can be applied to subterms.

A ≡ A′
cngkndpi_{1}
Πx:A. K ≡ Πx:A′. K

K ≡ K′
cngkndpi_{2}
Πx:A. K ≡ Πx:A. K′

A ≡ A′ ; cngfamapp_{1} ; A M ≡ A′ M  M ≡ M′ ; cngfamapp_{2} ; A M ≡ A M′
A_{1} ≡ A′_{1}
cngfampi_{1}

Πx:A_{1}. A_{2} ≡ Πx:A′_{1}. A_{2}

A_{2} ≡ A′_{2}
cngfampi_{2}
Πx:A_{1}. A_{2} ≡ Πx:A_{1}. A′_{2}
A ≡ A′
cngobjlam_{1}

λx:A. M ≡ λx:A′. M

M ≡ M′
cngobjlam_{2}
λx:A. M ≡ λx:A. M′
M_{1} ≡ M′_{1}
cngobjapp_{1}
M_{1} M_{2} ≡ M′_{1} M_{2}

M_{2} ≡ M′_{2}
cngobjapp_{2}
M_{1} M_{2} ≡ M_{1} M′_{2}

Some important properties of the LF type theory are stated at the end of next section.

3.9 Canonical Forms in LF

The notion of a canonical form, which is central to the representation theorems for LF encodings, is somewhat more complicated in full LF than in the simply typed fragment given in Section 3.1. In particular, we need to introduce auxiliary judgments for canonical types. At the same time we replace the rules with an indeterminate number of premisses by using another auxiliary judgment which establishes that an object is atomic, that is, of the form x M_{1} … M_{n} or c M_{1} … M_{n}, and its arguments M_{1}, …, M_{n} are again canonical. An analogous judgment exists at the level of families. Thus we arrive at the judgments

Γ ⊢_{Σ} M ⇑ A M is canonical of type A
Γ ⊢_{Σ} A ⇑ type A is a canonical type
Γ ⊢_{Σ} M ↓ A M is atomic of type A
Γ ⊢_{Σ} A ↓ K  A is atomic of kind K

These are defined by the following inference rules.

Γ ⊢_{Σ} A ⇑ type Γ, x:A ⊢_{Σ} M ⇑ B

canpi

Γ ⊢_{Σ} λx:A. M ⇑ Πx:A. B

Γ ⊢_{Σ} A ↓ type Γ ⊢_{Σ} M ↓ A

canatm

Γ ⊢_{Σ} M ⇑ A

Γ ⊢_{Σ} M ⇑ A  A ≡ B  Γ ⊢_{Σ} B : type

cancnv

Γ ⊢_{Σ} M ⇑ B

Σ(c) = A ; atmcon ; Γ ⊢_{Σ} c ↓ A Γ(x) = A ; atmvar ; Γ ⊢_{Σ} x ↓ A
Γ ⊢_{Σ} M ↓ Πx:A. B Γ ⊢_{Σ} N ⇑ A

atmapp

Γ ⊢_{Σ} M N ↓ [N/x]B

Γ ⊢_{Σ} M ↓ A  A ≡ B  Γ ⊢_{Σ} B : type

atmcnv

Γ ⊢_{Σ} M ↓ B

The conversion rules are included here for the same reason they are included among the inference rules for valid types and terms.

Σ(a) = K ; attcon ; Γ ⊢_{Σ} a ↓ K

Γ ⊢_{Σ} A ↓ Πx:B. K Γ ⊢_{Σ} M ⇑ B

attapp

Γ ⊢_{Σ} A M ↓ [M/x]K

Γ ⊢_{Σ} A ↓ K  K ≡ K′  Γ ⊢_{Σ} K′ Kind

attcnv

Γ ⊢_{Σ} A ↓ K′

Γ ⊢_{Σ} A ⇑ type Γ, x:A ⊢_{Σ} B ⇑ type

cntpi

Γ ⊢_{Σ} Πx:A. B ⇑ type

Γ ⊢_{Σ} A ↓ type ; cntatm ; Γ ⊢_{Σ} A ⇑ type

We state, but do not prove a few critical properties of the LF type theory. Basic versions of the results are due to Harper, Honsell, and Plotkin [HHP93], but their seminal paper does not treat η-conversion. The theorem below is a consequence of results in [Sal90, Coq91, Geu92]. The proofs are quite intricate, because of the mutually dependent nature of the levels of objects and types and are beyond the scope of these notes.

Theorem 3.13 (Properties of LF) Assume Σ is a valid signature, and Γ a context valid in Σ. Then the following hold.

1. If Γ ⊢_{Σ} M ⇑ A then Γ ⊢_{Σ} M : A.

2. If Γ ⊢_{Σ} A ⇑ type then Γ ⊢_{Σ} A : type.

3. For each object M such that Γ ⊢_{Σ} M : A there exists a unique^{7} object M′ such that M ≡ M′ and Γ ⊢_{Σ} M′ ⇑ A. Moreover, M′ can be effectively computed.

4. For each type A such that Γ ⊢_{Σ} A : type there exists a unique^{7} type A′ such that A ≡ A′ and Γ ⊢_{Σ} A′ ⇑ type. Moreover, A′ can be effectively computed.

5. Type checking in the LF type theory is decidable.

3.10 Summary and Further Discussion

In this chapter we have developed a methodology for representing deductive systems and their meta-theory within the LF Logical Framework. The LF type theory is a refinement of the Church’s simply-typed λ-calculus with dependent types.

The cornerstone of the methodology is a technique for representing the expressions of a language, whereby object-language variables are represented by metalanguage variables. This leads to the notion of higher-order abstract syntax, since now syntactic operators which bind variables must be represented by corresponding binding operators in the meta-language. As a consequence, expressions which differ only in the names of bound variables in the object language are α-convertible in the meta-language. Furthermore, substitution can be modelled by β-reduction. These relationships are expressed in the form of an adequacy theorem for the representation which postulates the existence of a compositional bijection between object language expressions and meta-language objects of a given type. Ordinarily, the representation of abstract syntax of a language does not involve dependent, but only simple types. This means that the type of representations of expressions, which was exp in the example used throughout this chapter, is a type constant and not an indexed type family. We refer to such a constant as a family at level 0. We summarize the methodology in the following table.

7up to α-conversion, as usual

Object Language  Meta-Language
Syntactic Category  Level 0 Type Family
Expressions  exp : type
Variable  Variable
x  x
Constructor  Constant
⟨e_{1}, e_{2}⟩  pair ⌜e_{1}⌝ ⌜e_{2}⌝, where

pair : exp → exp → exp

Binding Constructor  Second-Order Constant
let val x = e_{1} in e_{2} letv ⌜e_{1}⌝ (λx:exp. ⌜e_{2}⌝), where

letv : exp → (exp → exp) → exp

An alternative approach, which we do not pursue here, is to use terms in a firstorder logic to represent Mini-ML expressions. For example, we may have a binary function constant pair and a ternary function constant letv. We then define a predicate exp which is true for expressions and false otherwise. This predicate is defined via a set of axioms. For example, ∀e_{1}. ∀e_{2}. exp(e_{1}) ∧ exp(e_{1}) ⊃ exp(pair(e_{1}, e_{2})). Similarly, ∀x. ∀e_{1}. ∀e_{2}. var(x) ∧ exp(e_{1}) ∧ exp(e_{2}) ⊃ exp(letv(x, e_{1}, e_{2})), where var is another predicate which is true on variables and false otherwise. Since firstorder logic is undecidable, we must then impose some restriction on the possible definitions of predicates such as exp or var in order to guarantee decidable representations. Under appropriate restrictions such predicates can then be seen to define types. A commonly used class are regular tree types. Membership of a term in such a type can be decided by a finite tree automaton [GS84]. This approach to representation and types is the one usually taken in logic programming which has its roots in first-order logic. For a collection of papers describing this and related approaches see [Pfe92]. The principal disadvantage of regular tree types in a first-order term language is that it does not admit representation techniques such as higher-order abstract syntax. Its main advantage is that it naturally permits subtypes. For example, we could easily define the set of Mini-ML values as a subtype of expressions, while the representation of values in LF requires an explicit judgment. Thus, we do not capture in LF that it is decidable if an expression is a value. Some initial work towards combining regular tree types and function types is reported in [FP91] and [Pfe93].

The second representation technique translates judgments to types and deductions to objects. This is often summarized by the motto judgments-as-types. This can be seen as a methodology for formalizing the semantics of a language, since semantic judgments (such as evaluation or typing judgments) can be given conveniently and elegantly as deductive systems. The goal is now to reduce checking of deductions to type-checking within the framework (which is decidable). For this reduction to work correctly, the simply-typed framework which is sufficient for abstract syntax in most cases, needs to be refined by type families and dependent function types. The index objects for type families typically are representations of expressions, which means that they are typed at level 0. We refer to a family which is indexed by objects typed at level 0 as a level 1 family. We can summarize this representation technique in the following table.

Object Language  Meta-Language
Semantic Judgment  Level 1 Type Family
e ↪ v  eval : exp → exp → type
Inference Rule  Constant Declaration
\begin{aligned}e ↪ v \\ & ev s \\ s e ↪ s v \end{aligned}

ev s :

ΠE:exp. ΠV:exp. eval E V

→ eval (s E) (s V)

Deduction  Well-Typed Object
Deductive System  Signature

An alternative to dependent types (which we do not pursue here) is to define predicates in a higher-order logic which are true of valid deductions and false otherwise. The type family eval, indexed by two expressions, then becomes a simple type eval and we additionally require a predicate valid. The logics of higher-order Horn clauses [NM90, Wol91] and hereditary Harrop formulas [MNPS91, Pau87] support this approach and the use of higher-order abstract syntax. They have been implemented in the logic programming language λProlog [NM88] and the theorem prover Isabelle [PN90]. The principal disadvantage of this approach is that checking the validity of a deduction is reduced to theorem proving in the meta-logic. Thus decidability is not guaranteed by the representation and we do not know of any work to isolate decidable classes of higher-order predicates which would be analogous to regular tree types. Hereditary Harrop formulas have a natural logic programming interpretation, which permits them to be used as the basis for implementing programs related to judgments specified via deductive systems. For example, programs for evaluation or type inference in Mini-ML can be easily and elegantly expressed in λProlog. In Chapter 4 we show that a similar operational interpretation is also possible for the LF type theory, leading to the language Elf.

The third question we considered was how to represent the proofs of properties of deductive systems. The central idea was to represent the function which is implicit in a constructive proof as a judgment relating deductions. For example, the proof that evaluation returns a value proceeds by induction over the structure of the deduction D :: e ↪ v. This gives rise to a total function f, mapping each D :: e ↪ v into a deduction P :: v Value. We then represent this function as a judgment D ⟹ P such that D ⟹ P is derivable if and only if f(D) = P. A strong adequacy theorem, however, is not available, since the mathematical proof is informal, and not itself introduced as a mathematical object. The judgment between deductions is then again represented in LF using the idea of judgments-as-types, although now the index objects to the representing family represent deductions. We refer to a family indexed by objects whose type is constructed from a level 1 family as a level 2 family. The technique for representing proofs of theorems about deductive systems which have been formalized in the previous step is summarized in the following table.

Object Language  Meta-Language
Informal Proof  Level 2 Type Family

Value Soundness vs : ΠE:exp. ΠV:exp.

eval E V → value V → type

Case in Structural Induction  Constant Declaration
Base Case for Axioms  Constant of Atomic Type
Induction Step  Constant of Functional Type

A decidable criterion on when a given type family represent a proof of a theorem about a deductive system is subject of current research [Roh96]. Some initial results and ideas are reported in [PR92] under the name of schema-checking.^{8}

An alternative to this approach is to work in a stronger type theory with explicit induction principles in which we can directly express induction arguments. This approach is taken, for example, in the Calculus of Inductive Constructions [CH88, CP88] which has been implemented in the Coq system [Hue89]. The disadvantage of this approach is that it does not coexist well with the techniques of higher-order abstract syntax and judgments-as-types, since the resulting representation types (for example, exp) are not inductively defined in the usual sense.

3.11 Exercises

Exercise 3.1 Consider a variant of the typing rules given in Section 3.1 where the rules var, con, lam, tcon, and ectx are replaced by the following rules.

⊢_{Σ} Γ Ctx Σ(c) = A

con′ Γ ⊢_{Σ} c : A

⊢_{Σ} Γ Ctx Γ(x) = A

var′ Γ ⊢_{Σ} x : A

Γ, x:A ⊢_{Σ} M : B

lam′ Γ ⊢_{Σ} λx:A. M : A → B

⊢ Σ Sig Σ(a) = type

tcon′
⊢_{Σ} a : type

⊢ Σ Sig ; ectx ; ⊢_{Σ} · Ctx

8[remark about implementation?]

In what sense are these two systems equivalent? Formulate and carefully prove an appropriate theorem. Exercise 3.2 Prove Theorem 3.1. Exercise 3.3 Prove Lemma 3.4 Exercise 3.4 Give LF representations of the natural semantics rules ev pair, ev fst, and ev snd (see Section 2.3). Exercise 3.5 Reconsider the extension of the Mini-ML language by unit, void, and disjoint sum type (see Exercise 2.6). Give LF representation for

1. the new expression constructors,

2. the new rules in the evaluation and value judgments, and

3. the new cases in the proof of value soundness. Exercise 3.6 Give the LF representation of the evaluations in Exercise 2.3. You may need to introduce some abbreviations in order to make it feasible to write it down. Exercise 3.7 Complete the definition of the representation function for evaluations given in Section 3.6. Exercise 3.8 Complete the definition of the judgment

\begin{matrix}D \\ e ↪ v \end{matrix} ⟹ \begin{matrix}P \\ v Value \end{matrix}

given in Section 3.7 and give the LF encoding of the remaining inference rules. Exercise 3.9 Formulate and prove a theorem which expresses that the rules lam′′ and app′′ in Section 3.5 are no longer necessary, if A → B stands for Πx:A. B for some x which does not occur in B. Exercise 3.10 State the rules for valid signatures, contexts, and kinds which were omitted in Section 3.8. Exercise 3.11 Formulate an adequacy theorem for the representation of evaluations which is more general than Theorem 3.9 by allowing free variables in the expressions e and v. Exercise 3.12 Show the case for ev app in the proof of Lemma 3.8. Exercise 3.13 Prove Theorem 3.10. Exercise 3.14 Prove Theorem 3.12. Exercise 3.15 Prove items 1 and 2 of Theorem 3.13. Exercise 3.16 [ An exercise characterizing canonical forms. ]