On this page:
5.1 Syntax
5.2 Dynamic semantics
5.3 Static semantics

5 The higher-order lambda calculus λ-ω

5.1 Syntax

The higher-order calculus λ-ω extends System F with type operators, that is, functions at the type level:

In addition to type variables, functions, and universal types (all from System F), we now have abstraction of types over types () and application of types to types ().

In order to avoid errors at the type computation level, we now have “types of types,” known as kinds:

There are two kind constructors. Kind is the kind of proper types, that is, types that can be the types of terms. Kind is the kind of a type operator that consumes types of kind and produces types of kind . For example, the type constructor has kind —apply it to a term with a type of kind , like , and you get , a type of kind , back.

Note that types in λ-ω are a copy of the terms of STLC in the type level, with one base kind .

Terms in λ-ω are the same as terms in System F, except that the type variable in is decorated with a kind:

In fact, all three forms that bind type variables decorate them with a kind.

5.2 Dynamic semantics

The dynamics for λ-ω are straightforward. Values and evaluation contexts are as in System F:

There are two reduction rules, as in System F, for applications of λs and instantiations of Λs:

5.3 Static semantics

λ-ω’s type system is more complex than what we’ve seen before for two reasons: we are now doing computation at the type level and we now need to kind-check types.

Kind checking tells us that a type is well-formed in a context that binds type variables to their kinds. However, we will use the same contexts for type checking, so we will bind term variables to types and type variables to kind in the same context:

Then the kinding rules are like STLC at the type level:

Note that and form types of kind from types of kind .

In order to type check terms, we need a notion of type equivalence that includes computation in types. The usual theoretical way to do this is to define equivalence as a congruence that includes beta reduction:

Then in the type rules, we would check that types that in other systems need to be equal are equivalent. However, to type check algorithmically, we instead define type computation in terms of type evaluation contexts and then β reduction over types:

Then to compare two types, we fully reduce both of them and compare for equality. This happens in the rules for application and instantiation.