(*************************************************************************)
(** The simply-typed lambda calculus in Coq. *)
(*************************************************************************)
(** An interactive tutorial on developing programming language metatheory.  This file uses the simply-typed lambda calculus (STLC) to demonstrate the locally nameless representation of lambda terms and cofinite quantification in judgments.  This tutorial concentrates on "how" to formalize STLC; for more details about "why" we use this style of development see: "Engineering Formal Metatheory", Aydemir, Chargu'eraud, Pierce, Pollack, Weirich. POPL 2008.  Tutorial authors: Brian Aydemir and Stephanie Weirich, with help from Aaron Bohannon, Nate Foster, Benjamin Pierce, Jeffrey Vaughan, Dimitrios Vytiniotis, and Steve Zdancewic.  Adapted from code by Arthur Chargu'eraud.  Draft of 7/14/2009 *)
(*************************************************************************)
(** * Contents - Syntax of STLC - Substitution - Free variables - Open - Local closure - Properties about basic operations - Cofinite quantification - Tactic support - Typing environments - Typing relation - Weakening - Substitution - Values and evaluation - Preservation - Progress - Additional properties - Renaming - Decidability of typechecking - Equivalence of exist fresh and cofinite  Solutions to exercises are in [STLCsol.v]. *)
(*************************************************************************)
(** First, we import a number of definitions from the Metatheory library (see Metatheory.v).  The following command makes those definitions available in the rest of this file.  This command will only succeed if you have already run "make" in the tutorial directory to compile the Metatheory library. *)
(*************************************************************************)
(** * Syntax of STLC *)
(*************************************************************************)
(** We use a locally nameless representation for the simply-typed lambda calculus, where bound variables are represented as natural numbers (de Bruijn indices) and free variables are represented as [atom]s.  The type [atom], defined in the MetatheoryAtom library, represents names.  Equality on names is decidable ([eq_atom_dec]), and it is possible to generate an atom fresh for any given finite set of atoms ([atom_fresh]). *)
Inductive typ : Set :=
| typ_base  : typ
| typ_arrow : typ -> typ -> typ.
Inductive exp : Set :=
| bvar : nat  -> exp
| fvar : atom -> exp
| abs  : typ -> exp  -> exp
| app  : exp  -> exp -> exp.
Coercion bvar : nat >-> exp.
Coercion fvar : atom >-> exp.
(** We declare the constructors for indices and variables to be coercions.  That way, if Coq sees a [nat] where it expects an [exp], it will implicitly insert an application of [bvar]; and similarly for [atom]s.  For example, we can encode the expression (\x:b. Y x) as below. Because "Y" is free variable in this term, we need to assume an atom for this name. *)
Parameter Y : atom.
Definition demo_rep1 := abs typ_base (app Y 0).
(** Note that because of the coercions we may write [abs (app Y 0)] instead of [abs (app (fvar Y) (bvar 0))].  Below is another example: the encoding of (\x:b. \y:b. (y x)). *)
Definition demo_rep2 := abs typ_base (abs typ_base (app 0 1)).
(** Exercise: Uncomment and then complete the definitions of the following lambda calculus terms using the locally nameless representation.  "two"     :    \s:b->b. \z:b. s (s z)  "COMB_K"  :    \x:b. \y:b. x  "COMB_S"  :    \x:b -> b -> b.\y:b -> b.\z:b. x z (y z)  *)
Definition two :=
(* SOLUTION *)
abs (typ_arrow typ_base typ_base)
(abs typ_base (app 1 (app 1 0))).
Definition COMB_K :=
(* SOLUTION *)
abs typ_base
(abs typ_base 1).
Definition COMB_S :=
(* SOLUTION *)
abs (typ_arrow typ_base (typ_arrow typ_base typ_base))
(abs (typ_arrow typ_base typ_base)
(abs (typ_base)
(app (app 2 0) (app 1 0)))).
(** There are two important advantages of the locally nameless representation: - Alpha-equivalent terms have a unique representation. We're always working up to alpha-equivalence. - Operations such as free variable substitution and free variable calculation have simple recursive definitions (and therefore are simple to reason about).  Weighed against these advantages are two drawbacks: - The [exp] datatype admits terms, such as [abs 3], where indices are unbound. A term is called "locally closed" when it contains no unbound indices. - We must define *both* bound variable & free variable substitution and reason about how these operations interact with each other. *)
(*************************************************************************)
(** * Substitution *)
(*************************************************************************)
(** Substitution replaces a free variable with a term.  The definition below is simple for two reasons: - Because bound variables are represented using indices, there is no need to worry about variable capture. - We assume that the term being substituted in is locally closed.  Thus, there is no need to shift indices when passing under a binder. *)
Fixpoint subst (z : atom) (u : exp) (e : exp)
{struct e} : exp :=
match e with
| bvar i => bvar i
| fvar x => if x == z then u else (fvar x)
| abs t e1 => abs t (subst z u e1)
| app e1 e2 => app (subst z u e1) (subst z u e2)
end.
(** The [Fixpoint] keyword defines a Coq function.  As all functions in Coq must be total.  The annotation [{struct e}] indicates the termination metric---all recursive calls in this definition are made to arguments that are structurally smaller than [e].  Note also that subst uses the notation [x == z] for decidable atom equality.  This notation is defined in the Metatheory library.  We define a notation for free variable substitution that mimics standard mathematical notation. *)
Notation "[ z ~> u ] e" := (subst z u e) (at level 68).
(** To demonstrate how free variable substitution works, we need to reason about atom equality. *)
Parameter Z : atom.
(* Check (Y == Z). *)
(** The decidable atom equality function returns a sum. If the two atoms are equal, the left branch of the sum is returned, carrying a proof of the proposition that the atoms are equal.  If they are not equal, the right branch includes a proof of the disequality.  The demo below uses three new tactics: - The tactic [simpl] reduces a Coq expression to its normal form. - The tactic [destruct (Y==Y)] considers the two possible results of the equality test. - The tactic [Case] marks cases in the proof script. It takes any string as its argument, and puts that string in the hypothesis list until the case is finished. *)
(** Take-home Exercise: We can use almost the same proof script as above to state how substitution works in the variable case. Try it on your own.  *)
(*************************************************************************)
(** * Free variables *)
(*************************************************************************)
(** The function [fv], defined below, calculates the set of free variables in an expression.  Because we are using a locally nameless representation, where bound variables are represented as indices, any name we see is a free variable of a term.  In particular, this makes the [abs] case simple. *)
Fixpoint fv (e : exp) {struct e} : atoms :=
match e with
| bvar i => empty
| fvar x => singleton x
| abs t e1 => fv e1
| app e1 e2 => (fv e1) `union` (fv e2)
end.
(** The type [atoms] represents a finite set of elements of type [atom].  The notation for infix union is defined in the Metatheory library. *)
(* Demo [fsetdec]  The tactic [fsetdec] solves a certain class of propositions involving finite sets. See the documentation in [FSetWeakDecide] for a full specification. *)
(** Exercise [subst_fresh] To show the ease of reasoning with these definitions, we will prove a standard result from lambda calculus: if a variable does not appear free in a term, then substituting for it has no effect.  HINTS: Prove this lemma by induction on [e].  - You will need to use [simpl] in many cases.  You can [simpl] everything everywhere (including hypotheses) with the pattern [simpl in *].  - Part of this proof includes a false assumption about free variables.  Destructing this hypothesis produces a goal about finite set membership that is solvable by [fsetdec]. *)
(*************************************************************************)
(** * Opening *)
(*************************************************************************)
(** Opening replaces an index with a term.  It corresponds to informal substitution for a bound variable, such as in the rule for beta reduction.  Note that only "dangling" indices (those that do not refer to any abstraction) can be opened.  Opening has no effect for terms that are locally closed.  Natural numbers are just an inductive datatype with two constructors: [O] (as in the letter 'oh', not 'zero') and [S], defined in Coq.Init.Datatypes.  Coq allows literal natural numbers to be written using standard decimal notation, e.g., 0, 1, 2, etc. The notation [k == i] is the decidable equality function for natural numbers (cf. [Coq.Peano_dec.eq_nat_dec]).  This notation is defined in the Metatheory library.  We make several simplifying assumptions in defining [open_rec].  First, we assume that the argument [u] is locally closed.  This assumption simplifies the implementation since we do not need to shift indices in [u] when passing under a binder.  Second, we assume that this function is initially called with index zero and that zero is the only unbound index in the term.  This eliminates the need to possibly subtract one in the case of indices.  There is no need to worry about variable capture because bound variables are indices. *)
Fixpoint open_rec (k : nat) (u : exp)(e : exp)
{struct e} : exp :=
match e with
| bvar i => if k == i then u else (bvar i)
| fvar x => fvar x
| abs t e1 => abs t (open_rec (S k) u e1)
| app e1 e2 => app (open_rec k u e1) (open_rec k u e2)
end.
(** Many common applications of opening replace index zero with an expression or variable.  The following definition provides a convenient shorthand for such uses.  Note that the order of arguments is switched relative to the definition above.  For example, [(open e x)] can be read as "substitute the variable [x] for index [0] in [e]" and "open [e] with the variable [x]." Recall that the coercions above let us write [x] in place of [(fvar x)]. *)
Definition open e u := open_rec 0 u e.
(** This next demo shows the operation of [open].  For example, the locally nameless representation of the term (\y. (\x. (y x)) y) is [abs (app (abs (app 1 0)) 0)].  To look at the body without the outer abstraction, we need to replace the indices that refer to that abstraction with a name.  Therefore, we show that we can open the body of the abs above with Y to produce [app (abs (app Y 0)) Y)]. *)

Lemma demo_open :
open (app (abs typ_base (app 1 0)) 0) Y =
(app (abs typ_base (app Y 0)) Y).