Library STLCImpl

Require Import Arith.
Require Import Axioms.
Require Import AuxLib.
Require Import Nominal.
Require Import STLC.
Require Import Wf_nat.

Module MakeSTLC <: STLC.

Basic definitions


Term variables.
Definition tmvar := mkAtomRec 0.
Definition tmvarP := mkAtomPset tmvar.

STLC types.
Inductive ty : Set :=
  | unit : ty
  | arrow : ty -> ty -> ty.

Definition tyP := mkIdPset tmvar ty.

Phi n is the set of all terms whose bound variables have index less than n. Thus, Phi 0 corresponds to those terms with no dangling bound variables. Free variables are still represented using names.
Inductive Phi : nat -> Set :=
  | pdot : forall (n : nat), Phi n
  | pfree : forall (n : nat), tmvar -> Phi n
  | pbound : forall (n i : nat), i < n -> Phi n
  | papp : forall (n : nat), Phi n -> Phi n -> Phi n
  | plam : forall (n : nat), ty -> Phi (S n) -> Phi n.

We need to take advantage of proof irrelevance when considering equality on Phi.
Lemma pbound_eq :
  forall (n i i': nat), (i = i') ->
  forall (p : i < n) (q : i' < n),
  pbound n i p = pbound n i' q.

Hint Resolve pbound_eq : core nominal.

Free variables.
Fixpoint Phi_fv (n : nat) (x : Phi n) {struct x} : aset tmvar :=
  match x with
    | pdot _ => empty
    | pfree _ a => singleton a
    | pbound _ _ _ => empty
    | papp _ s t => union (Phi_fv _ s) (Phi_fv _ t)
    | plam _ _ s => Phi_fv _ s
  end.

Implicit Arguments Phi_fv [n].

Size. The fact that 1 + x converts to S x is helpful when defining the recursion combinator below, since it proceeds by induction on natural numbers (the initial value being the size of a term).
Fixpoint Phi_size (n : nat) (x : Phi n) {struct x} : nat :=
  match x with
    | pdot _ => 1
    | pfree _ _ => 1
    | pbound _ _ _ => 1
    | papp _ s t => 1 + (Phi_size _ s) + (Phi_size _ t)
    | plam _ _ s => 1 + (Phi_size _ s)
  end.

Implicit Arguments Phi_size [n].

Lemma Phi_size_eq_O :
  forall (T : Type) (n : nat) (x : Phi n), Phi_size x = 0 -> T.

Lemma Phi_size_lt_O :
  forall (T : Type) (n : nat) (x : Phi n), Phi_size x <= 0 -> T.

We need ``weakening'' on terms in order to define substitution: Any term in Phi n is also a term in Phi (S n) modulo the proofs carried by pbound subterms.
Fixpoint Phi_weaken (n : nat) (x : Phi n) {struct x} : Phi (S n) :=
  match x in Phi n return Phi (S n) with
    | pdot _ => pdot _
    | pfree _ a => pfree _ a
    | pbound _ i pf => pbound _ i (le_S _ _ pf)
    | papp _ s t => papp _ (Phi_weaken _ s) (Phi_weaken _ t)
    | plam _ T s => plam _ T (Phi_weaken _ s)
  end.

Implicit Arguments Phi_weaken [n].

Case analysis. This lemma is useful when built-in tactics fail to provide similar functionality, for example when you want to analyze x : Phi (S n) and case fails.
Lemma Phi_case :
  forall (n : nat) (P : Phi n -> Type),
  (P (pdot n)) ->
  (forall a, P (pfree n a)) ->
  (forall i pf, P (pbound n i pf)) ->
  (forall s t, P (papp n s t)) ->
  (forall T s, P (plam n T s)) ->
  (forall x, P x).

Well-founded induction on size.
Lemma Phi_ind_size_multi :
  forall (P : forall (n : nat), Phi (S n) -> Type),
  (forall n x, (forall m y, Phi_size y < Phi_size x -> P m y) -> P n x) ->
  (forall n x, P n x).

Lemma Phi_ind_size_single :
  forall (n : nat) (P : Phi n -> Type),
  (forall x, (forall y, Phi_size y < Phi_size x -> P y) -> P x) ->
  (forall x, P x).

Permutations


Fixpoint Phi_perm
  (n : nat) (p : permt tmvar) (x : Phi n) {struct x} : Phi n
:=
  match x in Phi n return Phi n with
    | pdot _ => pdot _
    | pfree _ a => pfree _ (perm tmvarP p a)
    | pbound _ i pf => pbound _ i pf
    | papp _ s t => papp _ (Phi_perm _ p s) (Phi_perm _ p t)
    | plam _ T s => plam _ T (Phi_perm _ p s)
  end.

Implicit Arguments Phi_perm [n].

Definition PhiPermR (n : nat) : PsetT tmvar (Phi n).

Permutation preserves the size of a term.
Lemma Phi_size_perm :
  forall (n : nat) (p : permt tmvar) (x : Phi n),
  Phi_size (Phi_perm p x) = Phi_size x.

Abstraction and instantiation


Turn a free variable into a bound variable (an index). We turn a term in Phi n into a term in Phi (S n) and choose n as the new index.
Fixpoint abs
  (n : nat) (a : tmvar) (x : Phi n) {struct x}
  : Phi (S n)
:=
  match x in Phi n return Phi (S n) with
    | pdot _ => pdot _
    | pfree i a' =>
        if atom_eqdec tmvar a a' then pbound (S i) i (lt_n_Sn _)
        else pfree _ a'
    | pbound i m pf => pbound (S i) m (lt_S _ _ pf)
    | papp _ s t => papp _ (abs _ a s) (abs _ a t)
    | plam _ T s => plam _ T (abs _ a s)
  end.

Implicit Arguments abs [n].

Turn the greatest index (bound variable) in a term into a free variable. A more general version of this function would instantiate the index with an arbitrary term.
Definition inst_aux
  (n' : nat) (a : tmvar) (x : Phi n') (n : nat) (wf : n' = S n) : Phi n.

Definition inst (n : nat) (a : tmvar) (x : Phi (S n)) : Phi n :=
  inst_aux (S n) a x n (refl_equal _).

Implicit Arguments inst [n].

The following rewrites make it easier to work with inst. It is not usually helpful to unfold inst to inst_aux.
Lemma inst_papp :
  forall (n : nat) (a : tmvar) (s t : Phi (S n)),
  inst a (papp _ s t) = papp _ (inst a s) (inst a t).

Lemma inst_plam :
  forall (n : nat) (a : tmvar) (s : Phi (S (S n))) (T : ty),
  inst a (plam _ T s) = plam _ T (inst a s).

abs preserves the size of terms.
Lemma Phi_size_abs :
  forall (n : nat) (x : Phi n) (a : tmvar),
  Phi_size (abs a x) = Phi_size x.

inst preserves the size of terms.
Lemma Phi_size_inst :
  forall (n : nat) (x : Phi (S n)) (a : tmvar),
  Phi_size (inst a x) = Phi_size x.

Permutations commute with abs.
Lemma Phi_perm_abs :
  forall n p c (s : Phi n),
  Phi_perm p (abs c s) = abs (perm tmvarP p c) (Phi_perm p s).

Swapping pseudo-commutes with inst under certain conditions.
Lemma Phi_swap_inst :
  forall (n : nat) (x : Phi (S n)) (a c d : tmvar),
  ~ In c (Phi_fv x) -> ~ In d (Phi_fv x) ->
  perm (PhiPermR n) [(c, d)] (inst a x) = inst (perm tmvarP [(c, d)] a) x.

Swapping commutes with inst under certain conditions.
Lemma Phi_swap_inst_commute :
  forall (n : nat) (x : Phi (S n)) (a c d : tmvar),
  a <> c -> a <> d ->
  perm (PhiPermR n) [(c, d)] (inst a x) =
  inst a (perm (PhiPermR (S n)) [(c, d)] x).

inst is the inverse of abs.
Lemma inst_abs_inv :
  forall (n : nat) (x : Phi n) (a : tmvar), inst a (abs a x) = x.

abs is the inverse of inst under certain conditions.
Lemma abs_inst_inv :
  forall (n : nat) (x : Phi (S n)) (a : tmvar),
  ~ In a (Phi_fv x) -> abs a (inst a x) = x.

The free variables of abs a x are the obvious ones.
Lemma Phi_fv_abs :
  forall (n : nat) (x : Phi n) (a : tmvar),
  Phi_fv (abs a x) = remove a (Phi_fv x).

User-level abstraction for terms


Definition tm : Set := Phi 0.
Definition tmP := PhiPermR 0.
Definition dot : tm := pdot 0.
Definition var (a : tmvar) : tm := pfree 0 a.
Definition app (s t : tm) : tm:= papp 0 s t.
Definition lam (x : tmvar) (T : ty) (b : tm) : tm := plam 0 T (abs x b).

Permutations


Section STLCPermutations.
Variable p : permt tmvar.
Variable c : tmvar.
Variable T : ty.
Variables s t : tm.

Theorem perm_dot : perm tmP p dot = dot.

Theorem perm_var : perm tmP p (var c) = var (perm tmvarP p c).

Theorem perm_app :
  perm tmP p (app s t) = app (perm tmP p s) (perm tmP p t).

Theorem perm_lam :
  perm tmP p (lam c T s) = lam (perm tmvarP p c) T (perm tmP p s).

End STLCPermutations.

Free variables


Definition fvar : tm -> aset tmvar := Phi_fv (n:= 0).

Theorem fvar_dot : fvar dot = empty.

Theorem fvar_var : forall a, fvar (var a) = singleton a.

Theorem fvar_app : forall s t, fvar (app s t) = union (fvar s) (fvar t).

Theorem fvar_lam : forall a T s, fvar (lam a T s) = remove a (fvar s).

Alpha-equality


Lemma swap_in_abs :
  forall (n : nat) (s : Phi n) (a b : tmvar),
  b <> a ->
  ~ In a (Phi_fv (Phi_perm [(a, b)] s)) ->
  abs b (Phi_perm [(a, b)] s) = abs a s.

Lemma eq_lam_diff :
  forall a b T T' s s',
  T = T' ->
  a <> b ->
   ~ In b (fvar s) ->
  s = perm tmP [(a, b)] s' ->
  lam a T s = lam b T' s'.

Theorem eq_lam :
  forall a b T s,
   ~ In b (fvar s) ->
  lam a T s = lam b T (perm tmP [(a, b)] s).

Structural induction


Theorem tm_induction_weak :
  forall (P : tm -> Type),
  (P dot) ->
  (forall a, P (var a)) ->
  (forall s, P s -> forall t, P t -> P (app s t)) ->
  (forall a T s, P s -> P (lam a T s)) ->
  forall x : tm, P x.

Theorem tm_induction :
  forall (P : tm -> Type) (F : aset tmvar),
  (P dot) ->
  (forall a, P (var a)) ->
  (forall s, P s -> forall t, P t -> P (app s t)) ->
  (forall a, ~ In a F -> forall T, forall s, P s -> P (lam a T s)) ->
  forall x : tm, P x.

Recursion


Section Recursion.

Variables (R : Set) (RP : PsetT tmvar R).
Variable (f_dot : R).
Variable (f_var : tmvar -> R).
Variable (f_app : tm -> R -> tm -> R -> R).
Variable (f_lam : tmvar -> ty -> tm -> R -> R).
Variable (F : aset tmvar).
Variable (supp_dot : supports RP F f_dot).
Variable (supp_var : supports (tmvarP ^-> RP) F f_var).
Variable (supp_app : supports (tmP ^-> RP ^-> tmP ^-> RP ^-> RP) F f_app).
Variable (supp_lam : supports (tmvarP ^-> tyP ^-> tmP ^-> RP ^-> RP) F f_lam).
Variable (fcb :
  exists b, ~ In b F /\ forall x y z, freshP RP b (f_lam b x y z)).

Derive Dependent Inversion tm_inv with (Phi 0) Sort Set.

Lemma Phi_size_lt_papp_fun :
  forall n m s t, Phi_size (papp n s t) <= S m -> Phi_size s <= m.

Implicit Arguments Phi_size_lt_papp_fun [n m s t].

Lemma Phi_size_lt_papp_arg :
  forall n m s t, Phi_size (papp n s t) <= S m -> Phi_size t <= m.

Implicit Arguments Phi_size_lt_papp_arg [n m s t].

Lemma Phi_size_lt_plam :
  forall n m a T s, Phi_size (plam n T s) <= S m -> Phi_size (inst a s) <= m.

Implicit Arguments Phi_size_lt_plam [n m a T s].

Our goal here is to define recursive functions over terms by induction on their size. This translates to induction on a natural number which bounds the size of the term. All the arguments here are important: n is required since the function is defined by induction on the structure of n, x is the term we actually care about, and the proof ensures that we don't have to deal with non-sensical combinations of n and x.

The proof is of an le relation and not an eq relation due to how recF makes recursive calls. We could, in theory, use an eq relation if we were willing to define recF by well-founded induction on n, but that seems to pose its own problems.
Definition recF :
  forall (n : nat) (x : Phi 0), Phi_size x <= n -> R.

The particular n and proof supplied to recF don't matter.
Lemma recF_stable :
  forall (n m : nat) (x : tm) (pfn : Phi_size x <= n) (pfm : Phi_size x <= m),
  recF n x pfn = recF m x pfm.

Lemma recF_stable_gen :
  forall (n m : nat) (x y : tm), x = y ->
  forall (pfn : Phi_size x <= n) (pfm : Phi_size y <= m),
  recF n x pfn = recF m y pfm.

Hint Resolve recF_stable_gen : core.

Define the recursion combinator, as seen by the user. Most of the cases for how it evaluates are straightforward.
Definition tm_rec (x : tm) : R := recF (Phi_size x) x (le_n _).

Theorem tm_rec_dot : tm_rec dot = f_dot.

Theorem tm_rec_var : forall a, tm_rec (var a) = f_var a.

Theorem tm_rec_app :
  forall s t, tm_rec (app s t) = f_app s (tm_rec s) t (tm_rec t).

Proving the rewrite rule for the lam case requires that we prove the following two properties simultaneously. There may be an easier way, but at least it doesn't seem that this particular method is specific to the object-language at hand.

It's only at this point where we need to know the support of f_dot, f_var, f_app, and f_lam, and know that fcb holds.
Let rewrite_prop (n : nat) :=
  forall a T s, Phi_size (lam a T s) <= n ->
  ~ In a F -> tm_rec (lam a T s) = f_lam a T s (tm_rec s).

Let swap_prop (n : nat) :=
  forall x c d pf pf', ~ In c F -> ~ In d F ->
  perm RP [(c, d)] (recF n x pf) = recF n (perm tmP [(c, d)] x) pf'.

Lemma rewrite_to_swap :
  forall (n : nat), (forall m, m <= n -> rewrite_prop m) -> swap_prop n.

Lemma swap_to_rewrite :
  forall (n : nat), (forall m, m < n -> swap_prop m) -> rewrite_prop n.

Lemma rewrite_and_swap : forall m, rewrite_prop m /\ swap_prop m.

Theorem tm_rec_lam :
  forall a T s, ~ In a F -> tm_rec (lam a T s) = f_lam a T s (tm_rec s).

Theorem tm_rec_supp :
  supports (tmP ^-> RP) F tm_rec.

End Recursion.

Constructor distinctness


Section Distinctness.
Variables a b : tmvar.
Variables T : ty.
Variables s t q r : tm.

Theorem distinctness_dot_var: dot <> var a.

Theorem distinctness_dot_app : dot <> app s t.

Theorem distinctness_dot_lam : dot <> lam a T s.

Theorem distinctness_var_dot : var a <> dot.

Theorem distinctness_var_app : var a <> app s t.

Theorem distinctness_var_lam : var a <> lam b T s.

Theorem distinctness_app_dot : app s t <> dot.

Theorem distinctness_app_var : app s t <> var a.

Theorem distinctness_app_lam : app s t <> lam a T q.

Theorem distinctness_lam_dot : lam a T s <> dot.

Theorem distinctness_lam_var : lam a T s <> var b.

Theorem distinctness_lam_app : lam a T s <> app q r.

End Distinctness.

Injectivity for constructors


Some of the lemmas in this section are needed because the injection tactic fails to produce useful hypotheses.
Theorem injection_var : forall a b, var a = var b -> a = b.

Lemma injection_papp_fun :
  forall n s s' t t', papp n s t = papp n s' t' -> s = s'.

Theorem injection_app_fun : forall s s' t t', app s t = app s' t' -> s = s'.

Lemma injection_papp_arg :
  forall n s s' t t', papp n s t = papp n s' t' -> t = t'.

Theorem injection_app_arg : forall s s' t t', app s t = app s' t' -> t = t'.

Theorem injection_lam_ty :
  forall a a' T T' s s',
  lam a T s = lam a' T' s' ->
  (T = T').

Lemma injection_plam :
  forall (n : nat) (T T' : ty) (s s' : Phi (S n)),
  plam n T s = plam n T' s' -> s = s'.

Lemma abs_eq :
  forall (n : nat) (a : tmvar) (s s' : Phi n),
  abs a s = abs a s' -> s = s'.

Lemma abs_neq :
  forall (n : nat) (a a' : tmvar) (s s' : Phi n),
  a <> a' ->
  abs a s = abs a' s' ->
  ~ In a (Phi_fv s') /\ s = Phi_perm [(a, a')] s'.

Theorem injection_lam_body :
  forall a a' T T' s s',
  lam a T s = lam a' T' s' ->
  (a = a' /\ s = s') \/
  (a <> a' /\ ~ In a (fvar s') /\ s = perm tmP [(a, a')] s').

End MakeSTLC.

Index
This page has been generated by coqdoc