Lambda-Weak Omega
Abstract
This post is part of a series on how to reason about code. It describes \(\lambda \underline{\omega}\) (Lambda Weak Omega), a precursor to the formal language understood by Coq, an interactive proof assistant I'm learning. Prior posts:
- first, long-ago post here
- a concrete example of symbolic execution
- why I'm interested in type theory here
- the λ Calculus
- the Simply Typed Lambda Calculus
- the Polymorphic Lambda Calculus
This exposition follows that in "Type Theory & Formal Proof" by Nederpelt & Geuvers (abbreviated TTFP throughout), with help from "Lambda Calculi with Types" by Henk Barendregt and a nice video series entitled The Lambda Cube Unboxed.
Recap
We've introduced the λ Calculus as a formal model of general computation & we've added a type system in order to regularize the legal terms therein (this is the system \(\lambda\rightarrow\)). We've introduced the idea of derivations as a means to type-check & find terms in typed systems.
From there, we've begun to extend \(\lambda\rightarrow\) in different ways. In the last post, we introduced \(\lambda 2\), which allowed abstraction & application to terms as well as types. In this post, we're going to introduce the system \(\lambda \underline{\omega}\).
\(\lambda \underline{\omega}\)
Motivation
In \(\lambda 2\), we're allowed to abstract over a type variable in a term; e.g. \(\Lambda\alpha:\ast.\lambda x:\alpha.x\) means "given a type α, we have a function \(\lambda x:\alpha.x\)." One may wonder: why not perform other operations on types? For example, we could write \(\lambda\alpha:\ast.\alpha\rightarrow\alpha\) for the operation "fix a type α: then return the type \(\alpha\rightarrow\alpha\)."
One might think of this as a function on types, which of course begs the question: what is the type of such a function? Well, if α is a type, then we already know that \(\alpha\rightarrow\alpha\) is a type. Given that it maps α to \(\alpha\rightarrow\alpha\), and \(\alpha:\ast\) as well as \(\alpha\rightarrow\alpha:\ast\), we would say \(\lambda\alpha:\ast.\alpha\rightarrow\alpha:\ast\rightarrow\ast\). This means that instead of the singular "supertype" ∗, alongside it, we have another supertype \(\ast\rightarrow\ast\). But if we're going to allow functions on types, there is now really an infinity of them, for instance \(\ast\rightarrow\ast, (\ast\rightarrow\ast)\rightarrow\ast, (\ast\rightarrow\ast)\rightarrow(\ast\rightarrow\ast)\) and so on.
Call the class of such supertypes kinds and denote it \(\mathbb{K}\). If we "hoist" our original type-making rules from \(\lambda\rightarrow\) to the new world of functions on types, we could say:
\begin{equation} \mathbb{K}:=\ast|(\mathbb{K}\rightarrow \mathbb{K}) \end{equation}We introduce a new symbol, \(\square\), for the set of all kinds. If \(M:\kappa,\kappa\in \mathbb{K}\) then we call M a type constructor, and we consider our familiar "simple" types such as α or \(\alpha\rightarrow\alpha\) to be type constructors of kind ∗.
We see that our world has opened up considerably: whereas before we had terms & types, we now have terms, types, kinds & \(\square\), and judgements turn into judgement chains:
\begin{align} &\text{Level 1}& &\text{Level 2} & &\text{Level 3} & & \text{Level 4}\nonumber\\ &x &: \alpha &: &\ast &: &\square \nonumber\\ & & \lambda\alpha:\ast.M &: &\ast\longrightarrow\ast &: &\square \end{align}Derivation Rules
The Formation & Conversion Rules
We first saw a formation rule in \(\lambda 2\), again because the set of terms was fixed a priori in \(\lambda\rightarrow\). There, the formation rule governed the addition of types to the context, albeit only by requiring that they were in \(\mathbb{T}2\). Here again, that process becomes more complex in \(\lambda \underline{\omega}\): we need to be sure that new types are well-formed:
\begin{equation} \textbf{(form) } \frac{\Gamma\vdash A:s \enspace \Gamma\vdash B:s}{\Gamma\vdash A\rightarrow B} \end{equation}where again \(s\) denotes either sort ∗ or \(\square\). This is what allows us to add e.g. \(\alpha\rightarrow\beta\) as a type to the context, assuming \(\alpha\) & \(\beta\) have been added as types previously (and \(\ast\rightarrow\ast\) analagously).
In addition, we'd like our typing to respect β-conversion at the type level. Even though we haven't discussed it, β-reduction works in the natural way for types in \(\lambda \underline{\omega}\): \((\lambda\alpha:\ast.\alpha\rightarrow\alpha)\beta\rightarrow_{\beta}\beta\rightarrow\beta\), for instance. We would like to be able to derive \(\Gamma\vdash A:B^{\prime}\) when we know \(\Gamma\vdash A:B\) and \(B\equiv_{\beta}B^{\prime}\):
\begin{equation} \textbf{(conv) } \frac{\Gamma\vdash A:B \enspace \Gamma\vdash B^{\prime}:s}{\Gamma\vdash A:B^{\prime}}\text{ if }B\equiv_{\beta}B^{\prime} \end{equation}(appl) and (abst)
These two rules now operate at both the level of terms & types, but I trust the generalizations will be obvious:
\begin{align} &\textbf{(appl) } &\frac{\Gamma\vdash M:A\rightarrow B \enspace \Gamma\vdash N:A}{\Gamma\vdash MN:B}\nonumber\\ \nonumber\\ &\textbf{(abst) } &\frac{\Gamma,x:A\vdash M:B \enspace \Gamma\vdash A\rightarrow B:s}{\Gamma\vdash\lambda x:A.M:A\rightarrow B} \end{align}More Derivations
For convenience, here are the seven derivation rules for \(\lambda \underline{\omega}\):
\begin{align} &\textbf{(sort) } &\emptyset\vdash\ast:\square \nonumber\\ \nonumber\\ &\textbf{(var) } &\frac{\Gamma\vdash A:s}{\Gamma,x:A\vdash x:A}\text{ if }x\not\in\Gamma \nonumber\\ \nonumber\\ &\textbf{(weak) } &\frac{\Gamma\vdash A:B \enspace \Gamma\vdash C:s}{\Gamma,x:C\vdash A:B}\text{ if }x\not\in\Gamma \nonumber\\ \nonumber\\ &\textbf{(form) } &\frac{\Gamma\vdash A:s \enspace \Gamma\vdash B:s}{\Gamma\vdash A\rightarrow B} \nonumber\\ \nonumber\\ &\textbf{(appl) } &\frac{\Gamma\vdash M:A\rightarrow B \enspace \Gamma\vdash N:A}{\Gamma\vdash MN:B}\nonumber\\ \nonumber\\ &\textbf{(abst) } &\frac{\Gamma,x:A\vdash M:B \enspace \Gamma\vdash A\rightarrow B:s}{\Gamma\vdash\lambda x:A.M:A\rightarrow B} \nonumber\\ &\textbf{(conv) } &\frac{\Gamma\vdash A:B \enspace \Gamma\vdash B^{\prime}:s}{\Gamma\vdash A:B^{\prime}}\text{ if }B\equiv_{\beta}B^{\prime} \end{align}Let's work an example (this is execise 4.5 in TTFP)– let's validate the judgement
\begin{equation} \alpha:\ast,x:\alpha\vdash\lambda y:\alpha.x:(\lambda\beta:\ast.\beta\rightarrow\beta)\alpha \end{equation}So this is obviously true: the term \(\lambda y:\alpha.x\) has type \(\alpha\rightarrow\alpha\) in a context in which \(x:\alpha\), and \((\lambda\beta:\ast.\beta\rightarrow\beta)\alpha\rightarrow_{\beta}\alpha\rightarrow\alpha\). The question is: how to derive it?
Well, we can see a few things immediately:
Figure 7: Example derivation in \(\lambda \underline{\omega}\) – First Steps
As an aside, I've taken a liberty with the step on line (4)– as stated, the weakening rule only allows the newly-added term (to the context) to be of kind ∗ or \(\square\), and I'm adding the term \(y:\alpha\). I have an e-mail out to the author & will update if I hear anything back. I the meantime, I'm not terribly worried since this seems "morally" correct– weakening a context should preserve things we already know.
We know that the type of the term of \(\lambda y:\alpha.x\) on line (5) is β-equivalent to that on line (7), so what do we have to show in order to set ourselves up for an application of (conv)? Well, if we interpret \(B^{\prime}\) as \((\lambda y:\beta:\ast.\beta\rightarrow\beta)\alpha\), we need to show that that type is well-formed in \(\lambda \underline{\omega}\)… but that term can easily be built-up in the familiar manner; it's just that we're operating on types instead of values now:
Figure 8: Example derivation in \(\lambda \underline{\omega}\) – Concluded
Where Are We?
We've augmented \(\lambda\rightarrow\) to be able to write functions on types as well as terms; hence the mnemonic "types depending on types". This has led to a noticably more compex derivation system. As to its expressiveness, I confess to being even more perplexed than I was with respect to \(\lambda 2\). A Guide to Typed Lambda Calculus states that \(\lambda \underline{\omega}\) "boils down to simply typed lambda calculus with user-defined types," so perhaps its not much more expressive than \(\lambda\rightarrow\) after all.
I think part of the reason I've had a hard time finding much on this is that \(\lambda \underline{\omega}\) seems to be not terribly interesting in its own right. Rather, its prominence is due to its location on Barendregt's "lambda cube", a very nice unification of many typed λ Calculii we'll be getting to in a subsequent post. More typically, \(\lambda \underline{\omega}\) is combined with \(\lambda 2\) to obtain \(\lambda\omega\), a more powerful system.
So far, we've extended \(\lambda\rightarrow\) with "terms depending on types" (in \(\lambda 2\)) and "types depending on types" (here in \(\lambda \underline{\omega}\)). That leaves "types depending on terms", otherwise known as "dependent types", which will be the subject of the next post.
05/27/24 19:23