hazelgrove / hazelnut-dynamics-agda

mechanization paired with https://github.com/hazelgrove/hazelnut-dynamics
MIT License
21 stars 3 forks source link

hazelnut-dynamics-agda

This repository is the mechanization of the work described in our POPL19 paper. It includes all of the definitions and proofs from Section 3, as claimed in Sec. 3.4 (Agda Mechanization).

How To Check These Proofs

These proofs are known to check under Agda 2.6.2. The most direct, if not the easiest, option to check the proofs is to install that version of Agda or one compatible with it, download the code in this repo, and run agda all.agda at the command line.

Alternatively, we have provided a Docker file to make it easier to build that environment and check the proofs. To use it, first install Docker, make sure the Docker daemon is running, and clone this repository to your local machine. Then, at a command line inside that clone, run

docker build -t hazel-popl19 .

This may take a fair amount of time. When it finishes, run

docker run hazel-popl19

This should take less than a minute, produce a lot of output as Agda checks each module and function, and end with either the line Finished all. or Loading all (/all.agdai). to indicate success, depending on Docker-level caching.

Most text editors that support Agda can be configured to use the version instead a Docker container instead of your host machine, so you can experiment with or evolve this code without making too much of a mess. For some example instructions, see the docker-agda repo.

Where To Find Each Theorem

All of the judgements defined in the paper are given in core.agda. The syntax is meant to mirror the on-paper notation as closely as possible, with some small variations because of the limitations of Agda syntax.

For easy reference, the proofs for the theorems in order of appearance in the paper text can be found as follows:

The extended paper with an appendix goes into more detail for some lemmas and definitions omitted from the main paper, some of which have been mechanized as well. Those can be found as follows:

Description of Agda Files

The theorem statements rely on a variety of lemmas and smaller claims or observations that aren't explicitly mentioned in the paper text. What follows is a rough description of what to expect from each source file; more detail is provided in the comments inside each.

On paper, we typically take it for granted that we can silently α-rename terms to equivalent terms whenever a collision of bound names is inconvenient. In a mechanization, we do not have that luxury and instead must be explicit in our treatment of binders in one way or another. In our development here, we assume that all terms are in an α-normal form where binders are globally not reused.

That manifests in this development where we have chosen to add premises that binders are unique within a term or disjoint between terms when needed. These premises are fairly benign, since α-equivalence tells us they can always be satisfied without changing the meaning of the term in question. Other standard approaches include using de Bruijn indices, Abstract Binding Trees, HOAS, or PHOAS to actually rewrite the terms when needed. We have chosen not to use these techniques because almost all of the theory we're interested in does not need them and their overhead quickly becomes pervasive, obfuscating the actual points of interest.

Similarly, we make explicit some premises about disjointness of contexts or variables being apart from contexts in some of the premises of some rules that would typically be taken as read in an on-paper presentation. This is a slightly generalized version of Barendrecht's convention (Barendregt, 1984), which we also used in our POPL17 mechanization for the same reason.

Since the type system for external terms is bidirectional, the judgments defining it are mutually recursive. That means that anything type-directed is very likely to also be mutually recursive. The grammar of internal expressions is also mutually recursive with the definition of substitution environments. All told, a fair number of theorems are mutually recursive as this percolates through. We try to name things in a suggestive way, using x-synth and x-ana for the two halves of a theorem named x.

Both hole and type contexts are encoded as Agda functions from natural numbers to optional contents. In practice these mappings are always finite. We represent finite substitutions and substitution environments explicitly as inductive datatypes, _,_⊢_:s:_and env from core.agda respectively, taking advantage of the fact that the base case in our semantics is always the identity substitution. This allows us to reason about substitutions in a well-founded way that passes the Agda termination checker.

Postulates

We have benign postulates in two places:

There are no other postulates in this development.

Meta

Prelude and Datatypes

These files give definitions and syntactic sugar for common elements of type theory (sum types, products, sigmas, etc.) and natural numbers that are used pervasively throughout the rest of the development.

Core Definitions

Structural Properties

Theorems

Canonical Forms

Together, these files give the canonical forms lemma for the language.

Metatheory of Type Assignment

Metatheory of Elaboration

Type Safety

These files contain proofs of type safety for the internal language. Note that we only give a dynamic semantics for the internal language, which is related to the external language through elaboration.

We also argue that our dynamics is a conservative extension in the sense that if you use it to evaluate terms that happen to have no holes in them, you get the standard type safety theorems you might expect for the restricted fragment without holes.

Metatheory of Continuity

Lemmas and Smaller Claims

These files each establish smaller claims that are either not mentioned in the paper or mentioned only in passing. In terms of complexity and importance, they're somewhere between a lemma and a theorem.

These files contain technical lemmas for the corresponding judgement or theorem. They are generally not surprising once stated, although it's perhaps not immediate why they're needed, and they tend to obfuscate the actual proof text. They are corralled into their own modules in an effort to aid readability.