(*************************************************************************)
(** 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]. *)

(* Take-home Demo: Prove that free variables are not introduced by substitution.  This proof actually is very automatable ([simpl in *; auto.] takes care of all but the fvar case), but the explicit proof below demonstrates two parts of the finite set library. These two parts are the tactic [destruct_notin] and the lemma [notin_union], both defined in the module [FSetWeakNotin].  Before stepping through this proof, you should go to that module to read about those definitions and see what other finite set reasoning is available.  *)
Lemma subst_notin_fv : forall x y u e,
x `notin` fv e -> x `notin` fv u ->
x `notin` fv ([y ~> u]e).