On this page:
3.1 Syntax
3.2 Dynamic semantics
3.3 Static semantics
3.3.1 Type safety
3.4 An extension
3.5 Normalization
3.6 Adding nontermination

3 The simply-typed lambda calculus λ-st🔗

3.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.

3.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.

3.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 15. 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 16. 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.

3.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 :

  • : Then trivially, with = .

  • : By inspection of the typing rules, we know that if has a type, that type is . By inversion, we know that has type as well. Then by the induction hypothesis, has some type , and for any having type , . Then by rule [succ], .

  • : The whole term has a type only if there is some type such that and . Then by the induction hypothesis, , and for any term having type , . Then .

  • : The whole term has a type only if there is some type such that and . Then by the induction hypothesis, , and for any term having type , . Then .

Lemma (Substitution). If and then .

Proof. By induction on the type derivation for :

  • : If = then = , and = , which has type . If , then = , which must type in .

  • : This types in an environment.

  • : Then it must be the case that . Then by induction, , and reapplying rule [succ], we have that .

  • cases are similar.

  • : This can be the case only if . Without loss of generality, , so we can swap them, yielding . Then by induction . Then apply rule [abs], to get .

Lemma (Preservation). If and then .

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

  • . By the replacement lemma, we know that . This only types if and . The former is only the case if . Then by the substitution lemma, , and by replacement, .

QED.

Lemma (Canonical forms).

If then:

  • If is , then is either or or for some .

  • If is , then is some lambda abstraction .

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:

  • : Vacuous, open terms don’t type.

  • : A value.

  • : By induction, steps or is a value of type . If the former then the whole term steps; if the latter then the whole term is a value.

  • : By induction, each subterm steps or is a value. If the first subterm steps, then the whole term steps. If the first subterm is a value and the second steps, then the whole thing steps. If both are values, then by inversion of the [app] rule, has a function type, and by the canonical forms lemma, that means it is a lambda abstraction . Then the whole term steps to .

  • : A value.

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

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

3.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 18. Implement multiplication using the recursor.

Exercise 19. Implement factorial using the recursor.

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

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

Exercise 22. 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.

3.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:

  • iff and .

  • iff and and for all such that , .

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

Lemma (SN preserved by conversion).

Suppose that and . Then:

  • implies .

  • implies .

Proof. By induction on :

  • : If normalizes then normalizes by the same sequence because takes a step to , which then normalizes. We know this because our formulation of λ-st is determistic, and there is no other way to reduce the term. (We could prove this but haven’t.) Since it has type , we have

    If normalizes then it does so via , so normalizes as well and by preservation it has the same type, so .

  • : If then we know that normalizes and when applied to a good term, that normalizes too. We need to show that does that same, that is, that implies that for arbitrary term . We know that . Since , we know that . Since is a subexpression of , we can apply the induction hypothesis at that type, yielding as desired.

    If then we know that normalizes and when applied to a good term, that normalizes too. We need to know that does the same, that is, that implies that for arbitrary term . We know that . Since , we know that . Then by induction, .

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:

  • : Appling to gets us a such that .

  • : Since = , and and , we have that .

  • : By inversion, we know that . Then by induction, we have that . By the definition of NT for , we have that types in the empty context and reduces to a natural number. Then does as well.

  • : By inversion, we know that and . By induction, we know that and . The former means that for any such that , we have . Let be . Then .

  • : Without loss of generality, let be fresh for . So then that term equals . We need to show that . To show this, we need to show three things:
    • To show . It suffices to show that for some such that , by the mass substitution lemma. That is what we have.

    • To show . This is clear, because it is a value.

    • To show that for any such that , . By the definition of SN, we know that for some value . Then we can take an additional step, . Because SN is preserved by backward conversion, it suffices to show that this term is SN for .

      By the lemma that SN is preserved by forward conversion, we know that . So then we can say that . Now consider inverting the judgment that . From this, we know that . Then applying the induction hypothesis, we have that . This is what we sought to show.

QED.

Strong normalization follows as a corollary.

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

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

3.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 26. Define addition, multiplication, and factorial using and . How does it compare to using the recursor?

Exercise 27. Extend type safety for .

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

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