4

I'm using Coq 8.5pl1.

To make a contrived but illustrative example,

(* fix so simpl will automatically unfold. *)
Definition double := fix f n := 2*n.

Theorem contrived n : double (2 + n) = 2 + double (1 + n).

Now, I only want to simplify the arguments to double, and not any part outside of it. (For example, because the rest has already carefully been put into the correct form.)

simpl.
   S (S (n + S (S (n + 0)))) = S (S (S (n + S (n + 0))))

This converted the outside (2 + ...) to (S (S ...)) as well as unfolding double.

I can match one of them by doing:

match goal with | |- (double ?A) = _ => simpl A end.
   double (S (S n)) = 2 + double (1 + n)

How do I say that I want to simplify all of them? That is, I want to get

   double (S (S n)) = 2 + double (S n)

without having to put a separate pattern for each call to double.

I can simplify except for double itself with

remember double as x; simpl; subst x.
   double (S (S n)) = S (S (double (S n)))
Cœur
  • 37,241
  • 25
  • 195
  • 267
scubed
  • 307
  • 3
  • 10

2 Answers2

5

Opaque/Transparent approach

Combining the results of this answer and this one, we get the following solution:

Opaque double.
simpl (double _).
Transparent double.

We use the pattern capability of simpl to narrow down its "action domain", and Opaque/Transparent to forbid (allow resp.) unfolding of double.

Custom tactic approach

We can also define a custom tactic for simplifying arguments:

(* simplifies the first argument of a function *)
Ltac simpl_arg_of function :=
  repeat multimatch goal with
         | |- context [function ?A] =>
              let A' := eval cbn -[function] in A in
                change A with A'
         end.

That let A' := ... construction is needed to protect nested functions from simplification. Here is a simple test:

Fact test n :
    82 + double (2 + n)
  =
    double (1 + double (1 + 20)) + double (1 * n).
Proof.
  simpl_arg_of double.

The above results in

82 + double (S (S n)) = double (S (double 21)) + double (n + 0)

Had we used something like context [function ?A] => simpl A in the definition of simpl_arg_of, we would've gotten

82 + double (S (S n)) = double 43 + double (n + 0)

instead.

Arguments directive approach

As suggested by @eponier in comments, we can take advantage of yet another form of simpl -- simpl <qualid>, which the manual defines as:

This applies simpl only to the applicative subterms whose head occurrence is the unfoldable constant qualid (the constant can be referred to by its notation using string if such a notation exists).

The Opaque/Transparent approach doesn't work with it, however we can block unfolding of double using the Arguments directive:

Arguments double : simpl never.
simpl double.
eponier
  • 3,062
  • 9
  • 20
Anton Trunov
  • 15,074
  • 2
  • 23
  • 43
  • This takes advantage of that both arguments happen to have the same form. That is not in general true. I want to pattern match on that they are arguments of the function double, not that it happens to be a sum involving n. (I probably should have picked a better example to make this more clear.) – scubed Jan 03 '17 at 02:16
  • Sorry, I misread you question. Please see the updated version. I hope I got it right this time. – Anton Trunov Jan 03 '17 at 16:11
  • @eponier `simpl double` will unfold `double` as well, but the OP wants to prevent this from hapenning. If you meant to place it between `Opaque`/`Transparent`, then `simpl double.` results in `Error: Cannot coerce double to an evaluable reference.`, and `simpl (double).` doesn't do anything (unsurprisingly). – Anton Trunov Jan 03 '17 at 18:17
  • @AntonTrunov Ok sorry I did not test what I suggested. I thought that it would work between `Opaque` and `Transparent` but indeed it fails with the error you mentioned. – eponier Jan 04 '17 at 08:55
  • @eponier My wild guess is that `simpl` internally calls `unfold`, which gives that error. But `Arguments double : simpl never. simpl double.` works in this case. – Anton Trunov Jan 04 '17 at 09:00
  • Thank you. The multimatch...context was the one I was looking for. Also, Opaque and Transparent look handy. Combining the transparent idea with qualified simpl, you could also do: `remember double as x; simpl (x _); subst x` . – scubed Jan 05 '17 at 06:54
2

You may find the ssreflect pattern selection language and rewrite mechanism useful here. For instance, you can have a finer grained control with patterns + the simpl operator /=:

From mathcomp Require Import ssreflect.
Definition double := fix f n := 2*n.
Theorem contrived n : double (2 + n) = 2 + double (1 + n).
rewrite ![_+n]/=.

Will display:

n : nat
============================
double (S (S n)) = 2 + double (S n)

You can also use anonymous rewrite rules:

rewrite (_ : double (2+n) = 2 + double (1+n)) //.

I would personally factor the rewrite in smaller lemmas:

Lemma doubleE n : double n = n + n.
Proof. by elim: n => //= n ihn; rewrite -!plus_n_Sm -plus_n_O. Qed.

Lemma doubleS n : double (1 + n) = 2 + double n.
Proof. by rewrite !doubleE /= -plus_n_Sm. Qed.

Theorem contrived n : double (1+n) = 2 + double n.
rewrite doubleS.
ejgallego
  • 6,709
  • 1
  • 14
  • 29
  • I'm using plain coq and not extra libraries at the moment. Though, if it's a library, the basic idea should translate. I'm not familiar with ssreflect syntax, so I'm guessing at the meanings. It looks like your first rewrite is looking for a sum with n, instead of that it is an argument. I don't entirely follow the anonymous rewrite part. It looks like you're talking about simplifying each element independently? That's what I'm trying to automate away. I want a way to pattern match on all of them, and simplify all of them automatically. – scubed Jan 03 '17 at 02:28
  • Many of us use ssreflect because of fundamental limitations with Coq tactics, in particular selection on rewriting tactics is one of them. That is to say, there are some things that are not possible to do within standard Coq. You can get a more detailed overview of syntax in the ssreflect manual, but basically `rewrite [P]E` means "rewrite with (possibly quantified) equation `E` on patterns matching `P`. The `!` means, "select all matching patterns", but can use many other fancy things, such as contextual patterns, etc... – ejgallego Jan 03 '17 at 11:13
  • Anonymous rewrite is similar to the `change` tactic in Coq, but you have access to all the fancy pattern selection rules, etc... It will generate a proof obligation, which is solved by conversion `//` in this case. – ejgallego Jan 03 '17 at 11:14