On this page:
2.1 Syntax
2.2 Dynamic semantics
2.3 Static semantics
2.3.1 Type safety
2.4 An extension
2.5 Normalization
2.6 Adding nontermination

2 The simply-typed lambda calculus λ-st

2.1 Syntax

The λ-st language has types and terms defined as follows:

Types include the natural numbers and function types . Terms include variables, Peano naturals ( for zero and for successor), lambda abstractions, and applications.

2.2 Dynamic semantics

To define the dynamic semantics of λ-st, we give syntax for values and evaluation contexts:

Values include natural numbers and lambda abstractions. We evaluate under and we evaluate both the operator and operand in an application.

Then the reduction relation consists of one rule:

The dynamic semantics of λ-st is given by the evaluation function eval:

eval() =

 

if

As defined, eval could be partial, but we will prove it total on well typed terms, first by proving that well typed terms don’t go wrong, and then by proving that well typed terms don’t diverge.

2.3 Static semantics

To type λ-st, we define typing contexts mapping variables to types:

Then the rules are as follows. There are two constructors for the naturals, and they type as such:

That is, is a natural, and for any term of type , has type as well.

Variables type by looking them up in the typing context:

Lambda abstractions type by extending the typing context with the bound variable and checking the body:

And finally, applications require the domain of the operator to match the type of the operand:

Exercise 8. Extend λ-st with a product type . You will need a form for constructing products and projections for getting the components out. Add the necessary reduction and typing rules.

Exercise 9. Extend λ-st with a sum type . You will need two injection forms and to create sums, and one case analysis form to eliminate them, . The case analysis form takes a step once reduces to a sum value: , and similarly for . Add the necessary reduction and typing rules.

2.3.1 Type safety

Before we can prove type safety, we need to prove several standard lemmas.

We use the judgment with no context to mean that types in an empty context: .

Lemma (Replacement). If then for some . Furthermore, for any other term , .

Proof. By induction on the structure of :

Lemma (Substitution). If and then .

Proof. By induction on the type derivation for :

Lemma (Preservation). If and then .

Proof. By cases on the reduction relation. There is one case:

QED.

Lemma (Canonical forms).

If then:

Proof. By induction on the structure of the typing derivation. Only three rules form values, and those three rules correspond to the conditions of the lemma.

Lemma (Progress). If then either is a value or for some .

Proof. By induction on the structure of the typing derivation, considering the terms:

Theorem (Safety). 1) If and then . 2) If then either is a value or for some .

Exercise 10. Extend the type safety theorem to cover product and/or sum types.

2.4 An extension

As it stands, we can’t do much with natural numbers. Inspired by Gödel’s system T, we add a limited, terminating form of recursion on natural numbers. We extend the syntax of terms and evaluation contexts as follows:

The new form is the recursor, which works as follows. First, it evaluates to a value, which must be a natural number. If that number is zero, then it evaluates . Otherwise, if that term is a successor , it recurs on , binding the recursive result to and the predecessor to in .

There are no new types. We extend the reduction relation with two cases representing the just-described dynamics:

There is one rule for typing the new form:

Here is predecessor expressed using the recursor:

pred =

For zero it returns zero, and for any other number it returns the predecessor, ignoring the recursive result.

And here is addition expressed using the recursor:

add =

Exercise 11. Implement multiplication using the recursor.

Exercise 12. Implement factorial using the recursor.

Exercise 13. Implement a function that divides a natural number by two (rounding down).

Exercise 14. Extend the type safety theorem for the recursor.

Exercise 15. The recursor is currently call-by-name, in the sense that it substitutes the whole recursive expression of for in the non-zero case. The call-by-value form would compute the value of that subterm first and then subtitute the value, but making it call-by-value requires introducing an additional form. will do. (Why can’t we just use λ?) Show how to add to λ-st and how that can be used to make the recursor call-by-value.

2.5 Normalization

A normal form is a form that doesn’t reduce any further, which for our purposes (since we have eliminated stuck states) is a value. A term normalizes, written if it reduces to a normal form, that is, a value.

Historically, when working with lambda calculi that allow free conversion (that is, redexes can by identified anywhere in a term, without a notion of evaluation contexts) authors have distinguished weak from strong normalization. A term weakly normalizes if it has some reduction sequence reaching a normal form; a term strongly normalizes if every of its reduction sequences reaches a normal form. However, we’ve defined our language to be deterministic, which causes weak and strong normalization to coincide.

We wish to show that all terms that have a type reduce to a value. It is insufficient to do induction on typing derivations. (Shall we try it?) What we end up needing is a (unary) relation on terms, indexed by types, and defined by induction on types, of the form , as follows:

Exercise 16. How would we extend for product and/or sum types?

Lemma (SN preserved by conversion).

Suppose that and . Then:

Proof. By induction on :

QED.

Next, we define substitutions, and what it means for a substitution to satisfy a typing environment. A substitution associates some variables with values to substitute them:

To apply a substitution to a term, written , is to substitute in the term the values of the substitution for their variables.

A substitution satisfies a typing environment if they have the same domains (sets of variables) and every value in the substitution not only has the type given for the corresponding variable in the type environment, but is SN for that type:

Note that if a substitution satisfies a type environment, this means that it contains values that typed in the empty type environment, meaning they are closed. Thus, the order of substitution doesn’t matter, as no variable in the substitution will interfere with any other.

Now we can prove a lemma that if we apply a substitution to a term that types in an environment consistent with the substitution, then the substituted term types in the empty environment:

Lemma (Mass substitution). If and then .

Proof. By induction on the length of . If empty, then is empty, and the substitution has no effect. Otherwise, = , where = and and . Then by the substitution lemma, . Then by induction, . But that is .

Lemma (Every typed term is good). If and then .

Proof. By induction on the typing derivation:

QED.

Strong normalization follows as a corollary.

Exercise 17. Extend the normalization proof to cover products and/or sums.

Exercise 18. Show that λ-st with the recursor still enjoys normalization.

2.6 Adding nontermination

We can add unrestricted recursion to λ-st by adding a fixed-point operator. We will also add an construct, which lets us discriminate between zero and non-zero and extracts the predecessor from a natural. (This is redundant with the recursor, but easier to use. The resulting language is equivalent to the classic PCF.)

The new expression forms are and :

What does at run time is apply its argument, which must be a function, to the of itself, thus implementing recursion. What does is discriminate between and , evaluating to its then-branch if zero, and its else-branch if not.

To type , we type its argument, which must be a function from the desired type to itself. To type , the natural position must type as , and the then- and else-branches must have the same type, where the else-branch has the predecessor bound.

Exercise 19. Define addition, multiplication, and factorial using and . How does it compare to using the recursor?

Exercise 20. Extend type safety for .

Exercise 21. Where does the normalization proof break down if we add ?

Exercise 22. Implement a union-find in STLC with records and vectors.