Section CofiniteQuantification.
Local Notation "{ k ~> u } t" := (open_rec k u t) (at level 67).
(* With this new definition, we can almost use the same proof for [open_rec_lc], we only need one change. We need to add the line [pick fresh x for L.]  immediately before the use of [apply open_rec_lc_core].  This tactic, defined in [Metatheory], introduces a new atom [x] that is known not to be in the set [L]. *)
(* Take-home Exercise: The next two lemmas have exactly the same proofs as before. *)
(*************************************************************************)
(*                                                                       *)
(*  Coffee break                                                         *)
(*                                                                       *)
(*************************************************************************)
(*************************************************************************)
(** * Typing environments *)
(*************************************************************************)
(** We represent environments as association lists (lists of pairs of keys and values) whose keys are [atom]s. *)
Notation env := (list (atom * typ)).
(** For STLC, environments bind [atom]s to [typ]s.  We define an abbreviation [env] for the type of these environments.  Coq will print [list (atom * typ)] as [env], and we can use [env] as a shorthand for writing [list (atom * typ)].  Lists are defined in Coq's standard library, with the constructors [nil] and [cons].  The list library includes the [::] notation for cons as well as standard list operations such as append, map, and fold. The infix operation "++" is list append.  The Metatheory library extends this reasoning by instantiating the AssocList library to provide support for association lists whose keys are [atom]s.  Everything in this library is polymorphic over the type of objects bound in the environment.  Look in AssocList for additional details about the functions and predicates that we mention below. *)
(** Environment equality *)
(** When reasoning about environments, we often need to talk about bindings in the "middle" of an environment. Therefore, it is common for lemmas and definitions to use list append in their statements. Unfortunately, list append is associative, so two Coq expressions may denote the same environment even though they are not equal.  The tactic [simpl_env] reassociates all concatenations of environments to the right. *)
(** To make environments easy to read, instead of building them from [nil] and [cons], we prefer to build them from the following components: - [nil]: The empty list. - [one]: Lists consisting of exactly one item. - [++]:  List append.  Furthermore, we introduce compact notation for one (singleton lists): [(x ~ T)] is the same as [one (x, T)]. *)
(** The simpl_env tactic actually puts lists built from only nil, one and [++] into a "normal form". This process reassociates all appends to the right, removes extraneous nils converts cons to singleton lists with an append. *)
(** Note that the [simpl] tactic doesn't produce the "normal form" for environments. It should always be followed up with [simpl_env].  Furthermore, to convert an environment to any equivalent form other than the normal form (perhaps to apply a lemma) use the tactic [rewrite_env]. *)
(** Environment operations. *)
(** The ternary predicate [binds] holds when a given binding is present somewhere in an environment. *)
(** The function [dom] computes the domain of an environment, returning a finite set of [atom]s. Note that we cannot use Coq's equality for finite sets, we must instead use a defined relation [=] for atom set equality. *)
(** The unary predicate [uniq] holds when each atom is bound at most once in an environment. *)

Lemma uniq_demo : forall (x y : atom) (T : typ),
x <> y -> uniq ((x ~ T) ++ (y ~ T)).