Search code examples
coqcoq-tactic

How can I write a tactic which works both in a goal and a hypothesis?


I am trying to write a tactic which can work on goals and hypothesis similar to symmetry, but for inequalities as well. Now, there is some documentation on generalized rewriting, but this is very advanced and perhaps should be a different question. That said, here is my implementation which works (albeit awkwardly) for my use case:

Require Import Coq.PArith.BinPos.

Ltac symmetry' :=
  lazymatch goal with
  | [ |- ?x <> ?y ] => unfold not; intros NQ; symmetry in NQ; revert NQ
  | [ |- _ ] => symmetry
  end.

Lemma succ_discr' : forall p : positive,
  Pos.succ p <> p.
Proof.
  intros p.
  symmetry'.
  apply Pos.succ_discr.
Qed.

This is fine, but what if we have an inequality in the hypothesis we wish to apply symmetry' to?


Lemma succ_discr' : forall p : positive,
  Pos.succ p <> p -> (2 * p <> Pos.succ (2 * p))%positive.
Proof.
  intros p H.
  Fail symmetry' in H.
  (* Fails with Error:
Syntax error: [tactic:ltac_use_default] expected after [tactic:tactic] (in [vernac:tactic_command]). *)

I tried writing

Ltac symmetry'' H :=
  lazymatch H with
   | ?x <> ?y => unfold not; intros NQ; symmetry in NQ; revert NQ
   | _ => symmetry
   end.

then applying via symmetry'' H but that failed with another error,

Error:
Tactic failure:  The relation (fun x y : positive => x <> y) is not a
declared symmetric relation. Maybe you need to require the
Coq.Classes.RelationClasses library.

So, my question is, is there something special about the builtin symmetry tactic which allows it to be used with the in keyword, or do I just need to write my Ltac in a particular way to be able to use in?


Solution

  • Ltac symmetry' :=
      lazymatch goal with
      | [ |- ?x <> ?y ] => apply not_eq_sym
      | [ |- _ ] => symmetry
      end.
    
    Ltac symmetry'' H :=
      lazymatch type of H with
       | ?x <> ?y => apply not_eq_sym in H
       | _ => symmetry in H
       end.
    

    Note that you need to match on type of H, not on H itself (since it's going to be a variable).

    is there something special about the builtin symmetry tactic which allows it to be used with the in keyword, or do I just need to write my Ltac in a particular way to be able to use in?

    The in variant of a tactic can be defined as a Tactic Notation (i.e., it's really a separate thing from the original tactic).

    Tactic Notation "symmetry'" "in" ident(H) := symmetry'' H.
    (* symmetry' in H *)