LinearTactics - coq/coq GitHub Wiki

I tried to have a small set of tactics which clean the hypothesis they use:

  • clean (removes some hypothesis and recursively all dependent hypothesis)
  • purge/purge_goal (same job as clean but fails if ALL dependent hypothesis cannot be cleared, so purge_goal is a good cleaner, but also very dangerous as you can get silently rid of hypothesis)
  • linear forward (an assert cleaning its hypothesis)
  • linear backward (an apply cleaning its hypothesis)

See and run the last example.

(** * cleaning tactic *)
Ltac clean H :=
 match H with
 | ?a ?b => clean a; clean b; try clean a
 | ?a _ => clean a
 | _ ?b => clean b
 | forall (a: ?A), ?B => clean B; clean A
 | ?A -> ?B => clean B; clean A
 | fun u => ?f => idtac "not tested feature"; clean f
 | ?x => clean_aux x
 end
with clean_aux H :=
 match type of H with
 | Type => try clear H
 | Prop => try clear H
 | ?t => try (clear H; clean t)
 end.
(** this tactics fails if it cannot completely clean some hypothesis *)
Ltac purge H :=
 match H with
 | ?a ?b => purge a; purge b
 | ?a ?b => purge b; purge a
 | forall (a: ?A), ?B => purge B; purge A
 | ?A -> ?B => purge B; purge A
 | fun u => ?f => idtac "not tested feature"; purge f
 | ?x => purge_aux x
 end
with purge_aux H :=
 match type of H with
 | Type => clear H
 | Prop => clear H
 | ?t => clear H; purge t
 end.
Ltac purge_goal :=
 repeat
 match goal with
 | H: _ |- _ => purge_aux H
 end.
(** * The forward and backward linear tactics *)
(** ** It performs an [assert] and [clean] all no more needed hypothesis *)
(** It uses same kind of hack as [exploit] tactic in Compcert *)
(** we don't care about induction principles generation;
    we do not use the predefined "list" type constructor,
    but it is cleaner not to do so, so the user cannot be
    confused *)
CoInductive TacList :=
| TacListNil
| TacListCons: forall A, A -> TacList -> TacList.
Notation "'forward' x .. y 'as'" :=
 (TacListCons _ x .. (TacListCons _ y TacListNil) .., true)
 (x at level 0).
Notation "'backward' x .. y" :=
 (TacListCons _ x .. (TacListCons _ y TacListNil) .., false)
 (x at level 0, at level 0).
Ltac fwx H t l :=
match l with
| TacListNil => let h := fresh H in
                assert (h := t);
                clean t;
                rename h into H
| TacListCons ?A ?a ?b =>
(  fwx H (t a) b
|| fwx H (t _ a) b
|| fwx H (t _ _ a) b
|| fwx H (t _ _ _ a) b
|| fwx H (t _ _ _ _ a) b
|| fwx H (t _ _ _ _ _ a) b
|| fwx H (t _ _ _ _ _ _ a) b
|| fwx H (t _ _ _ _ _ _ _ a) b)
end.
(** ** The backward tactic *)
(** *** not a very smart tactic; it is a [linear forward] followed by [apply] *)
Tactic Notation "linear" constr(t) :=
(match t with
 | (TacListCons ?A ?a ?l, false) => let H := fresh in
                                    fwx H a l;
                                    apply H; clean H
 | _ => fail "Sorry, I couldn't combine the given terms"
 end).
(** ** The forward tactic *)
Tactic Notation "linear" constr(t) ident(H) :=
(match t with
 | (TacListCons ?A ?a ?l, true) => fwx H a l
 | _ => fail "Sorry, I couldn't combine the given terms"
 end).
(** * Improved now *)
(** in Coq 8.2, "easy" can be slower than auto,
    so here is a patch to correct it *)
Tactic Notation "now" tactic (a) := (a; auto; easy).
Example cleaning_test:
 forall (A B C D E F: Prop),
  (A -> B -> C) ->
  ((B -> C) -> D -> E -> F) ->
  (A -> E) ->
  A -> D -> F.
Proof.
 intros A B C D E F HABC HBCDEF HAE HA HD.
 (* we will use two time A, so we need to duplicate it *)
 assert (A_backup := HA).
 linear forward HABC HA as HBC. (* now H and H2 do not exist anymore *)
 linear forward HAE A_backup as HE.
        (* now HAE and HE do not exist anymore, and neither does A *)
 linear backward HBCDEF. (* now HBCDEF does not exist anymore *)
   purge_goal. (* keeps all hypothesis depending on B and C *)
   assumption.
  clear -HD. (* clear all but D and HD *)
  assumption.
 easy.
 (* I know this tactic is sufficient to do all the job,
    but not for illustrate *)
Qed.