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.