coq-club AT inria.fr
Subject: The Coq mailing list
List archive
- From: Jonathan Leivent <jonikelee AT gmail.com>
- To: coq-club AT inria.fr
- Subject: Re: [Coq-Club] decide equality on more types
- Date: Fri, 8 Jul 2016 13:31:07 -0400
- Authentication-results: mail3-smtp-sop.national.inria.fr; spf=None smtp.pra=jonikelee AT gmail.com; spf=Pass smtp.mailfrom=jonikelee AT gmail.com; spf=None smtp.helo=postmaster AT mail-qk0-f181.google.com
- Ironport-phdr: 9a23:WWjvkxBT4LhHCs8VpKb5UyQJP3N1i/DPJgcQr6AfoPdwSP74osbcNUDSrc9gkEXOFd2CrakV06yN6+u5ATdIyK3CmUhKSIZLWR4BhJdetC0bK+nBN3fGKuX3ZTcxBsVIWQwt1Xi6NU9IBJS2PAWK8TWM5DIfUi/yKRBybrysXNWD14Lsj6vro8abSj4LrQL1Wal1IhSyoFeZnegtqqwmFJwMzADUqGBDYeVcyDAgD1uSmxHh+pX4p8Y7oGwD884mosVHSODxe7kyZb1eFjUvdW4vt+PxshyWbwyJ72ccW2NethdJHQXD8FmuXJD3syj3sudw8CafNMzyC7szXGLxvO9QVBb0hXJfZHYC+2bNh5kogQ==
On 07/08/2016 12:14 PM, Jonathan Leivent wrote:
On 07/08/2016 11:56 AM, Matthieu Sozeau wrote:
Hi Jonathan,
...
Both fin_eqdec and Fin3_eqdec are closed under the global context, as they
are using instances of decidable equality for nat and fin.
This eqdec_proof tactic is not able to handle your Finx example currently
though, it's also work in progress. How did you implement your tactic?
I am close to having a standalone demo version that uses the standard Eqdec_dep. So I will send this to you as soon I have it, which should be quite soon. It is not very big or complex.
Attached...
-- Jonathan
(*** A version of decide equality without axioms.*) (*Uses 8.5pl1 and may depend on fixes for things like congruence added afterwards - hence it may not work on an earlier build.*) Require Coq.Logic.Eqdep_dec. Global Set Injection On Proofs. Ltac revert_all := repeat lazymatch goal with H : _ |- _ => revert H end. (*Do induction on H, but make sure the induction is helpful - a quick check is that its first subgoal is not the same as the original goal. Note that this cheats a little in that it relies on progress not detecting the change to the shelf. *) Ltac helpful_induction H := unshelve (progress (induction H; [|shelve..])). Ltac default_handle_sub_deceq := clear;revert_all;shelve. Ltac handle_sub_deceq := default_handle_sub_deceq. (*Do injections, making sure they aren't the useless variety that just repeats the injecting hyp (which would cause an infinite loop) - also handle any sigma-based equalities that are either from some previous injection or from the pose_sigmaT mechanism below.*) Ltac do_injections := repeat match goal with | H : @eq (@sigT _ _) _ _ |- _ => apply Eqdep_dec.inj_pair2_eq_dec in H; [subst|default_handle_sub_deceq] | H : @eq (@ex _ _) _ _ |- _ => apply Eqdep_dec.inj_right_pair in H; [subst|default_handle_sub_deceq] | H : _ = _ |- _ => let H' := fresh in let tH := type of H in injection H as H'; let tH' := type of H' in (*if the result of the injection is no different, avoid it:*) tryif constr_eq tH tH' then fail else idtac; subst end. (*Given a hyp equality, which we really really want to subst, at least on the conclusion, try a bunch of tactics to make it subst:*) Ltac force_subst H := lazymatch type of H with | ?X = ?Y => first [subst X |subst Y |change X with Y |change Y with X |replace X with Y by congruence |replace Y with X by congruence |rewrite !H |rewrite <- !H |fail 1 "Cannot substitute" X "with" Y ] end. (*Given an eqdec conclusion, walk through the args of the compared term and try to discharge each via a sub eqdec:*) Ltac do_next_eqdec := idtac; (*avoid early eval*) let rec f pair := lazymatch pair with | (?A, ?A) => left; reflexivity | (?A ?X, ?A ?Y) => (*pick off first differnce*) let H := fresh in assert ({X=Y}+{X<>Y}) as [H|H]; [(*sub eqdec*) try (*check if there is an induction hypothesis that can be applied:*) match goal with IH :_ |- _ => apply IH; assumption end |(*H:X=Y case*)tryif force_subst H then do_next_eqdec else idtac |(*H:X<>Y case*)try (right; contradict H; do_injections; congruence)] | (?F _, ?G _) => f (F, G) | _ => try (right; discriminate) end in lazymatch goal with | |- ({?X=?Y}+{_}) => f (X, Y) | _ => fail end. (*Given a term (or type), return its head symbol and arity:*) Ltac head_arity term := let rec f x := lazymatch x with | (?F ?A, ?N) => f (F, S N) | _ => x end in f (term, 0). (*Pose a nested sigT for term as H. This is the basis of the mechanism that allows destruct on H when it is very dependently intertwined with the goal. Why this works: this nested sigT container can wrap a copy of term, but do so such that its type is not "co-dependent" with term - meaning that none of term's type indexes are part of this nested sigT's type. That's important because an equality between two such nested sigTs (both wrapping term), with just one side of the equality unfolded to expose the existTs, will maintain a validly typed equality between the destructing term and its generalized counterpart in the conclusion, even though some of term's type indexes change due to the destruct. *) Ltac pose_sigT term H := let rec f h n := lazymatch n with | 0 => h | S ?m => let a := fresh in constr:(sigT (fun a => ltac:(let h' := constr:(h a) in let r := f h' m in exact r))) end in let type := type of term in let ha := head_arity type in let s := lazymatch ha with (?h, ?n) => f h n end in simple refine (let H := _ : s in _); [repeat eapply existT; exact term|]. (*Kind of like "generalize_eqs X; destruct X", but using a nested sigT equality instead of a JMeq to maintain the equivalence between the destructee and the generalized copy of it in the conclusion:*) Ltac depdestruct H := let s := fresh in pose_sigT H s; generalize (eq_refl s); (*conclusion now is "s=s -> ..."*) unfold s at 1; (*unfold only one side of the above s=s equality to make it's H visible*) generalize H; (*all H's visible in the goal are generalized away from being dependent on H*) destruct H; subst s; (*intro just the 2 generalizations above - the first should come back as H if all dependencies on H were removed via the generalization (to force that, first do "revert dependent H; intro H" prior to calling depdestruct)*) intros ? ?; subst; do_injections. (*start an eqdec proof - trying both the non-dependent and dependent mechanisms:*) Ltac start_eqdec := intros; clear; lazymatch goal with |- ({?a=?b}+{_}) => generalize a, b; let X := fresh in intro X; tryif helpful_induction X then (let Y := fresh in intro Y; first[destruct Y | depdestruct Y]) else fail end. (*Do just one level of eqdep, leaving any sub-eqdeps:*) Ltac decide_one_equality := try unshelve (start_eqdec;do_next_eqdec;handle_sub_deceq). (*Because the above can generate many like-typed eqdec subgoals, instead of recursively dealing with each separately, we would like to combine them to leave fewer. The following is a generic global tactic to combine like-typed subgoals together. This is quite a bit of a hack, but it isn't really necessary either. Somewhat unfortunately, the refines used to name and rename goals will also induce typeclass search - and so any subgoals that can get filled that way will be, even before all like subgoals can be combined. This can get fixed by wrapping the subgoals with something before calling refine on them < TODO.*) Ltac combine_subgoals := let ng := numgoals in do ng (try refine ?[_Combine_subgoals]; try solve[intros;apply ?_Combine_subgoals]; refine _; cycle 1). (*Putting it all together. Note that each invocation of decide_one_equality does only one "level" of eqdec, leaving behind any sub-eqdec goals, which then get combined by combine_subgoals (or solved as typeclass instances). Hence, "repeat decide_one_equality" is a complete solution. However, note that the repeat tactical loses global focus, so not all resulting like-typed sub-eqdecs will get combined across subgoals if repeat is used. Also, note that we can't "sort" the subgoals so that the absolute minimum of sub-eqdecs are done. Unfortunately, because we want the iteration to give up when it can't make progress, and there is no current way to do this without losing global focus (see bug 3878 for lengthy discussion), that's the best we can currently do.*) Ltac decide_equality := repeat (decide_one_equality; combine_subgoals). (*** Examples:*) Inductive Fin : nat -> Set := | F1 : forall n : nat, Fin (S n) | FS : forall n : nat, Fin n -> Fin (S n). Lemma Fin_eqdec: forall n (a b : Fin n), {a=b}+{a<>b}. Proof. decide_equality. Qed. Print Assumptions Fin_eqdec. Inductive Fin3 : forall n (f1 f2 : Fin n), Set := | F31 : forall m (f1 f2 : Fin m), (f1 = f2) -> Fin3 (S m) (FS _ f1) (FS _ f2) | F3S : forall m (f1 f2 : Fin m), Fin3 m f1 f2 -> Fin3 (S m) (FS _ f1) (FS _ f2). Lemma Fin3_eqdec : forall n f1 f2 (a b : Fin3 n f1 f2), {a=b}+{a<>b}. Proof. (*Just to make a point about the usefullness of combine_subgoals, the following leaves 23 sub-eqdecs, but only 2 distinct ones:*) decide_one_equality. all:combine_subgoals. Restart. decide_equality. Qed. Print Assumptions Fin3_eqdec. Inductive Finx(n : nat) : Set := | Fx1(i : nat)(e : n = S i) | FxS(i : nat)(f : Finx i)(e : n = S i). Ltac handle_sub_deceq ::= (*Add ability to handle UIP cases - this could have been built-in, but doing it this way illustrates another way becides typeclasses to ofload eqdep subgoals:*) try (intros; left; apply Eqdep_dec.UIP_dec); default_handle_sub_deceq. Lemma Finx_eqdec : forall n (a b : Finx n), {a=b}+{a<>b}. Proof. (*Note that without the UIP addition above, this would leave an unsolved eqdec corresponding to FxS e field:*) decide_equality. Qed. Print Assumptions Finx_eqdec.
- [Coq-Club] decide equality on more types, Jonathan Leivent, 07/05/2016
- Re: [Coq-Club] decide equality on more types, Jason Gross, 07/05/2016
- Re: [Coq-Club] decide equality on more types, Jonathan Leivent, 07/05/2016
- Re: [Coq-Club] decide equality on more types, Jason Gross, 07/05/2016
- Re: [Coq-Club] decide equality on more types, Jonathan Leivent, 07/06/2016
- [Coq-Club] Ltac way to detect symbol definedness (was Re: decide equality on more types), Jonathan Leivent, 07/06/2016
- Re: [Coq-Club] decide equality on more types, Matthieu Sozeau, 07/08/2016
- Re: [Coq-Club] decide equality on more types, Jonathan Leivent, 07/08/2016
- Re: [Coq-Club] decide equality on more types, Jonathan Leivent, 07/08/2016
- Re: [Coq-Club] decide equality on more types, Jonathan Leivent, 07/08/2016
- Re: [Coq-Club] decide equality on more types, Jonathan Leivent, 07/08/2016
- Re: [Coq-Club] decide equality on more types, Jonathan Leivent, 07/06/2016
- Re: [Coq-Club] decide equality on more types, Jason Gross, 07/05/2016
- Re: [Coq-Club] decide equality on more types, Jonathan Leivent, 07/05/2016
- Re: [Coq-Club] decide equality on more types, Jason Gross, 07/05/2016
Archive powered by MHonArc 2.6.18.