(** You can "use theorems as functions" in this way with almost all tactics that take a theorem name as an argument.  Note also that theorem application uses the same inference mechanisms as function application; thus, it is possible, for example, to supply wildcards as arguments to be inferred, or to declare some hypotheses to a theorem as implicit by default.  These features are illustrated in the proof below. *)
Example lemma_application_ex :
forall {n : nat} {ns : list nat},
In n (map (fun m => m * 0) ns) ->
n = 0.
(** We will see many more examples of the idioms from this section in later chapters. *)
(* ################################################################# *)
(** * Coq vs. Set Theory *)
(** Coq's logical core, the _Calculus of Inductive Constructions_, differs in some important ways from other formal systems that are used by mathematicians for writing down precise and rigorous proofs.  For example, in the most popular foundation for mainstream paper-and-pencil mathematics, Zermelo-Fraenkel Set Theory (ZFC), a mathematical object can potentially be a member of many different sets; a term in Coq's logic, on the other hand, is a member of at most one type.  This difference often leads to slightly different ways of capturing informal mathematical concepts, but these are, by and large, quite natural and easy to work with.  For example, instead of saying that a natural number [n] belongs to the set of even numbers, we would say in Coq that [ev n] holds, where [ev : nat -> Prop] is a property describing even numbers.  However, there are some cases where translating standard mathematical reasoning into Coq can be either cumbersome or sometimes even impossible, unless we enrich the core logic with additional axioms.  We conclude this chapter with a brief discussion of some of the most significant differences between the two worlds. *)
(* ================================================================= *)
(** ** Functional Extensionality *)
(** The equality assertions that we have seen so far mostly have concerned elements of inductive types ([nat], [bool], etc.).  But since Coq's equality operator is polymorphic, these are not the only possibilities -- in particular, we can write propositions claiming that two _functions_ are equal to each other: *)
Example function_equality_ex1 : plus 3 = plus (pred 4).
(** Naturally, we must be careful when adding new axioms into Coq's logic, as they may render it _inconsistent_ -- that is, they may make it possible to prove every proposition, including [False]!  Unfortunately, there is no simple way of telling whether an axiom is safe to add: hard work is generally required to establish the consistency of any particular combination of axioms.  However, it is known that adding functional extensionality, in particular, _is_ consistent.  To check whether a particular proof relies on any additional axioms, use the [Print Assumptions] command.  *)
Print Assumptions function_equality_ex2.
(* ===> Axioms: functional_extensionality : forall (X Y : Type) (f g : X -> Y), (forall x : X, f x = g x) -> f = g *)
(** **** Exercise: 4 stars (tr_rev)  *)
(** One problem with the definition of the list-reversing function [rev] that we have is that it performs a call to [app] on each step; running [app] takes time asymptotically linear in the size of the list, which means that [rev] has quadratic running time. We can improve this with the following definition: *)
Fixpoint rev_append {X} (l1 l2 : list X) : list X :=
match l1 with
| [] => l2
| x :: l1' => rev_append l1' (x :: l2)
end.
Definition tr_rev {X} (l : list X) : list X :=
rev_append l [].
(** This version is said to be _tail-recursive_, because the recursive call to the function is the last operation that needs to be performed (i.e., we don't have to execute [++] after the recursive call); a decent compiler will generate very efficient code in this case.  Prove that the two definitions are indeed equivalent. *)

Lemma tr_rev_correct : forall X, @tr_rev X = @rev X.