comparison src/Universes.v @ 344:7466ac31f162

New section on avoiding axioms
author Adam Chlipala <adam@chlipala.net>
date Mon, 17 Oct 2011 14:56:46 -0400
parents be8c7aae20f4
children 518c8994a715
comparison
equal deleted inserted replaced
343:be8c7aae20f4 344:7466ac31f162
837 : fin (13 + 1) 837 : fin (13 + 1)
838 ]] 838 ]]
839 839
840 This simple computational reduction hides the use of a recursive function to produce a suitable [refl_equal] proof term. The recursion originates in our use of [induction] in [t4]'s proof. *) 840 This simple computational reduction hides the use of a recursive function to produce a suitable [refl_equal] proof term. The recursion originates in our use of [induction] in [t4]'s proof. *)
841 841
842
843 (** ** Methods for Avoiding Axioms *)
844
845 (** The last section demonstrated one reason to avoid axioms: they interfere with computational behavior of terms. A further reason is to reduce the philosophical commitment of a theorem. The more axioms one assumes, the harder it becomes to convince oneself that the formal system corresponds appropriately to one's intuitions. A refinement of this last point, in applications like %\index{proof-carrying code}%proof-carrying code%~\cite{PCC}% in computer security, has to do with minimizing the size of a %\index{trusted code base}\emph{%#<i>#trusted code base#</i>#%}%. To convince ourselves that a theorem is true, we must convince ourselves of the correctness of the program that checks the theorem. Axioms effectively become new source code for the checking program, increasing the effort required to perform a correctness audit.
846
847 An earlier section gave one example of avoiding an axiom. We proved that [pred_strong1] is agnostic to details of the proofs passed to it as arguments, by unfolding the definition of the function. A %``%#"#simpler#"#%''% proof keeps the function definition opaque and instead applies a proof irrelevance axiom. By accepting a more complex proof, we reduce our philosophical commitment and trusted base. (By the way, the less-than relation that the proofs in question here prove turns out to admit proof irrelevance as a theorem provable within normal Gallina!)
848
849 One dark secret of the [dep_destruct] tactic that we have used several times is reliance on an axiom. Consider this simple case analysis principle for [fin] values: *)
850
851 Theorem fin_cases : forall n (f : fin (S n)), f = First \/ exists f', f = Next f'.
852 intros; dep_destruct f; eauto.
853 Qed.
854
855 Print Assumptions fin_cases.
856 (** %\vspace{-.15in}%[[
857 Axioms:
858 JMeq.JMeq_eq : forall (A : Type) (x y : A), JMeq.JMeq x y -> x = y
859 ]]
860
861 The proof depends on the [JMeq_eq] axiom that we met in the chapter on equality proofs. However, a smarter tactic could have avoided an axiom dependence. Here is an alternate proof via a slightly strange looking lemma. *)
862
863 (* begin thide *)
864 Lemma fin_cases_again' : forall n (f : fin n),
865 match n return fin n -> Prop with
866 | O => fun _ => False
867 | S n' => fun f => f = First \/ exists f', f = Next f'
868 end f.
869 destruct f; eauto.
870 Qed.
871
872 (** We apply a variant of the %\index{convoy pattern}%convoy pattern, which we are used to seeing in function implementations. Here, the pattern helps us state a lemma in a form where the argument to [fin] is a variable. Recall that, thanks to basic typing rules for pattern-matching, [destruct] will only work effectively on types whose non-parameter arguments are variables. The %\index{tactics!exact}%[exact] tactic, which takes as argument a literal proof term, now gives us an easy way of proving the original theorem. *)
873
874 Theorem fin_cases_again : forall n (f : fin (S n)), f = First \/ exists f', f = Next f'.
875 intros; exact (fin_cases_again' f).
876 Qed.
877 (* end thide *)
878
879 Print Assumptions fin_cases_again.
880 (** %\vspace{-.15in}%
881 <<
882 Closed under the global context
883 >>
884
885 As another example, consider the following type of formulas in first-order logic. The intent of the type definition will not be important in what follows, but we give a quick intuition for the curious reader. Our formulas may include [forall] quantification over arbitrary [Type]s, and we index formulas by environments telling which variables are in scope and what their types are; such an environment is a [list Type]. A constructor [Inject] lets us include any Coq [Prop] as a formula, and [VarEq] and [Lift] can be used for variable references, in what is essentially the de Bruijn index convention. (Again, the detail in this paragraph is not important to understand the discussion that follows!) *)
886
887 Inductive formula : list Type -> Type :=
888 | Inject : forall Ts, Prop -> formula Ts
889 | VarEq : forall T Ts, T -> formula (T :: Ts)
890 | Lift : forall T Ts, formula Ts -> formula (T :: Ts)
891 | Forall : forall T Ts, formula (T :: Ts) -> formula Ts
892 | And : forall Ts, formula Ts -> formula Ts -> formula Ts.
893
894 (** This example is based on my own experiences implementing variants of a program logic called XCAP%~\cite{XCAP}%, which also includes an inductive predicate for characterizing which formulas are provable. Here I include a pared-down version of such a predicate, with only two constructors, which is sufficient to illustrate certain tricky issues. *)
895
896 Inductive proof : formula nil -> Prop :=
897 | PInject : forall (P : Prop), P -> proof (Inject nil P)
898 | PAnd : forall p q, proof p -> proof q -> proof (And p q).
899
900 (** Let us prove a lemma showing that a %``%#"#[P /\ Q -> P]#"#%''% rule is derivable within the rules of [proof]. *)
901
902 Theorem proj1 : forall p q, proof (And p q) -> proof p.
903 destruct 1.
904 (** %\vspace{-.15in}%[[
905 p : formula nil
906 q : formula nil
907 P : Prop
908 H : P
909 ============================
910 proof p
911 ]]
912 *)
913
914 (** We are reminded that [induction] and [destruct] do not work effectively on types with non-variable arguments. The first subgoal, shown above, is clearly unprovable. (Consider the case where [p = Inject nil False].)
915
916 An application of the %\index{tactics!dependent destruction}%[dependent destruction] tactic (the basis for [dep_destruct]) solves the problem handily. We use a shorthand with the %\index{tactics!intros}%[intros] tactic that lets us use question marks for variable names that do not matter. *)
917
918 Restart.
919 Require Import Program.
920 intros ? ? H; dependent destruction H; auto.
921 Qed.
922
923 Print Assumptions proj1.
924 (** %\vspace{-.15in}%[[
925 Axioms:
926 eq_rect_eq : forall (U : Type) (p : U) (Q : U -> Type) (x : Q p) (h : p = p),
927 x = eq_rect p Q x p h
928 ]]
929
930 Unfortunately, that built-in tactic appeals to an axiom. It is still possible to avoid axioms by giving the proof via another odd-looking lemma. Here is a first attempt that fails at remaining axiom-free, using a common equality-based trick for supporting induction on non-variable arguments to type families. The trick works fine without axioms for datatypes more traditional than [formula], but we run into trouble with our current type. *)
931
932 Lemma proj1_again' : forall r, proof r
933 -> forall p q, r = And p q -> proof p.
934 destruct 1; crush.
935 (** %\vspace{-.15in}%[[
936 H0 : Inject [] P = And p q
937 ============================
938 proof p
939 ]]
940
941 The first goal looks reasonable. Hypothesis [H0] is clearly contradictory, as [discriminate] can show. *)
942
943 discriminate.
944 (** %\vspace{-.15in}%[[
945 H : proof p
946 H1 : And p q = And p0 q0
947 ============================
948 proof p0
949 ]]
950
951 It looks like we are almost done. Hypothesis [H1] gives [p = p0] by injectivity of constructors, and then [H] finishes the case. *)
952
953 injection H1; intros.
954
955 (** Unfortunately, the %``%#"#equality#"#%''% that we expected between [p] and [p0] comes in a strange form:
956
957 [[
958 H3 : existT (fun Ts : list Type => formula Ts) []%list p =
959 existT (fun Ts : list Type => formula Ts) []%list p0
960 ============================
961 proof p0
962 ]]
963
964 It may take a bit of tinkering, but, reviewing Chapter 3's discussion of writing injection principles manually, it makes sense that an [existT] type is the most direct way to express the output of [inversion] on a dependently typed constructor. The constructor [And] is dependently typed, since it takes a parameter [Ts] upon which the types of [p] and [q] depend. Let us not dwell further here on why this goal appears; the reader may like to attempt the (impossible) exercise of building a better injection lemma for [And], without using axioms.
965
966 How exactly does an axiom come into the picture here? Let us ask [crush] to finish the proof. *)
967
968 crush.
969 Qed.
970
971 Print Assumptions proj1_again'.
972 (** %\vspace{-.15in}%[[
973 Axioms:
974 eq_rect_eq : forall (U : Type) (p : U) (Q : U -> Type) (x : Q p) (h : p = p),
975 x = eq_rect p Q x p h
976 ]]
977
978 It turns out that this familiar axiom about equality (or some other axiom) is required to deduce [p = p0] from the hypothesis [H3] above. The soundness of that proof step is neither provable nor disprovable in Gallina.
979
980 Hope is not lost, however. We can produce an even stranger looking lemma, which gives us the theorem without axioms. *)
981
982 Lemma proj1_again'' : forall r, proof r
983 -> match r with
984 | And Ps p _ => match Ps return formula Ps -> Prop with
985 | nil => fun p => proof p
986 | _ => fun _ => True
987 end p
988 | _ => True
989 end.
990 destruct 1; auto.
991 Qed.
992
993 Theorem proj1_again : forall p q, proof (And p q) -> proof p.
994 intros ? ? H; exact (proj1_again'' H).
995 Qed.
996
997 Print Assumptions proj1_again.
998 (** <<
999 Closed under the global context
1000 >>
1001
1002 This example illustrates again how some of the same design patterns we learned for dependently typed programming can be used fruitfully in theorem statements. *)