(*************************************************************************)
(** * 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.(*************************************************************************)
(** * 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.(*************************************************************************)
(** * 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.Inductive exp : Set :=
  | bvar : nat  -> exp
  | fvar : atom -> exp
  | abs  : typ -> exp  -> exp
  | app  : exp  -> exp -> exp.Inductive exp : Set :=
  | bvar : nat  -> exp
  | fvar : atom -> exp
  | abs  : typ -> exp  -> exp
  | app  : exp  -> exp -> exp.Inductive exp : Set :=
  | bvar : nat  -> exp
  | fvar : atom -> exp
  | abs  : typ -> exp  -> exp
  | app  : exp  -> exp -> exp.Inductive exp : Set :=
  | bvar : nat  -> exp
  | fvar : atom -> exp
  | abs  : typ -> exp  -> exp
  | app  : exp  -> exp -> 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)))).(** 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.

Lemma f_equal_demo : forall e1 e2, e1 = e2 -> fv e1 = fv e2.