On this page:
8.1 Syntax
8.2 Typing Rules

8 The Lambda Cube: λ-cube

The λ-cube provides a systematic organization of types systems that captures a range of expressiveness, from the simply-typed lambda calculus (in The simply-typed lambda calculus λ-st) through the polymorphic lambda calculus (in The polymorphic lambda calculus λ-2), the higher-order lambda calculus (in The higher-order lambda calculus λ-ω), and up to λC, the calculus of constructions, which is the focus of this section.

8.1 Syntax

The basic idea of the structure of the λ cube is to eliminate the distinction between types and terms and then use typing judgments to control which classes of expression are allowed in type positions. To get started, we first just get rid of the distinction between types and terms using this syntax:

The first three expression forms are the familiar variables, lambda abstractions, and application expressions. The is the type of types, just as in The polymorphic lambda calculus λ-2, and is analgous, but one level up. That is, it represents the type of kinds or, expressions that have the type are expressions that themselves compute kinds.

The final expression form, , represents function types, but it is dependent. In its simplest form, the type , where does not appear free in , represents functions from to . In general, however, the variable can appear free in , meaning that the type of the result of the function can depend on the argument actually supplied to the function.

This notation specializes to the earlier type systems we considered; as an example, recall the function composition operator from the beginning of The polymorphic lambda calculus λ-2. Here’s the original version of the function:

In the new language, the and are not distinguished by the constructor, but a is the same thing as a where the argument has type . So, this is the composition operator in λ-cube:

We also adjust the syntax to require an extra set of parentheses and a colon to make it a little bit easier to read expressions (because other distinctions are removed).

Another example that’s worth considering is the identity function. Here it is:

This term is what you would expect, simply replacing the capital Λ with the lowercase λ and adding a . But consider its type:

This is a type that cannot be expressed with just the arrow. Or, in other words, this is a dependent type because the variable bound by the outer function type is used in its body. It is the same as the type but we can use for both the function type and for the type.

8.2 Typing Rules

First, we just assert that is a

and then we have what appears to be the standard variable rule:

but note the premise that ensures that the environment is well-formed. In earlier type systems, that was a self-contained check that the types were well-formed. Now, because we have eliminated the distinction between types and terms, it uses the typing judgment itself:

The application rule handles all forms of abstraction:

It looks something like a combination of the application and type application rule from λ-2. Like the normal function application rule, we make sure that the two subexpressions have appropriate types: one a function and one a matching argument type (the type in the parameter of the function type). Like the type application rule, however, we perform a substitution, computing the type of the result of the function based on the argument that was actually supplied.

Sometimes, the type that we get on the function is different than the type on the argument. This rule allows us to do some computation in order to make two such types match up to each other, where the relation allows us to perform substitutions in the types as needed.

In order to type check a abstraction,

we check the body, on the assumption that the argument has the type on the . The second premise ensures that the type that we get for the result itself makes sense.

The final rule covers function type expressions.

This version allows either or for both the argument and the result type. This generality allows us to capture the full Calculus of Constructions, which forms the basis for the theorem proving system Coq.

If we restrict the rule so that both and are , then the resulting type system is equivalent to the simply-typed lambda calculus. Intuitively, this restriction means that our functions can accept arguments that are values, i.e., can be described by types, but not by kinds.

If we allow to be either or , but restrict so it can be only , we get the polymorphic lambda calculus, λ-2. This means that functions can now play the role of , expressions, accepting types, but always returning only a type. Various other restrictions in this spirit correspond to various other type systems in the literature.