aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEnrico Tassi2019-04-03 14:36:57 +0200
committerErik Martin-Dorel2019-04-23 20:22:36 +0200
commite1e5d40a0f351cbf01f7e66d049f9a937d5f8563 (patch)
tree562d194ad1ec0bbd15cdf3705d6b662648ca40b4
parent04da1739139c8d96469a2b86280e520e532beb39 (diff)
[doc] ssr_under: a few improvements
-rw-r--r--doc/sphinx/proof-engine/ssreflect-proof-language.rst214
1 files changed, 122 insertions, 92 deletions
diff --git a/doc/sphinx/proof-engine/ssreflect-proof-language.rst b/doc/sphinx/proof-engine/ssreflect-proof-language.rst
index c32788c9da..7545e1b8d7 100644
--- a/doc/sphinx/proof-engine/ssreflect-proof-language.rst
+++ b/doc/sphinx/proof-engine/ssreflect-proof-language.rst
@@ -3636,10 +3636,10 @@ rewriting rule is stated using Leibniz equality (as opposed to setoid
relations). It will be extended to other rewriting relations in the
future.
-.. _under_over_ssr:
+.. _under_ssr:
-A convenience under tactic
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+Rewriting under binders
+~~~~~~~~~~~~~~~~~~~~~~~
Goals involving objects defined with higher-order functions often
require "rewriting under binders". While setoid rewriting is a
@@ -3661,92 +3661,127 @@ complete terms, as shown by the simple example below.
.. coqtop:: in
Axiom subnn : forall n : nat, n - n = 0.
- Parameter G : (nat -> nat) -> nat -> nat.
- Axiom eq_G :
+ Parameter map : (nat -> nat) -> list nat -> list nat.
+ Parameter sumlist : list nat -> nat.
+ Axiom eq_map :
forall F1 F2 : nat -> nat,
(forall n : nat, F1 n = F2 n) ->
- forall n : nat, G F1 n = G F2 n.
+ forall l : list nat, map F1 l = map F2 l.
.. coqtop:: all
- Lemma example_G (n : nat) : G (fun n => n - n) n >= 0.
+ Lemma example_map l : sumlist (map (fun m => m - m) l) = 0.
- In this context, one cannot directly use ``eq_G``:
+ In this context, one cannot directly use ``eq_map``:
.. coqtop:: all fail
- rewrite eq_G.
+ rewrite eq_map.
as we need to explicitly provide the non-inferable argument ``F2``,
which corresponds here to the term we want to obtain *after* the
- rewriting step. We can thus do:
+ rewriting step. In order to perform the rewrie step one has to
+ provide the term by hand as follows:
.. coqtop:: all abort
- rewrite (@eq_G _ (fun _ : nat => 0)).
+ rewrite (@eq_map _ (fun _ : nat => 0)).
by move=> m; rewrite subnn.
- Otherwise, a more convenient approach consists in relying on the
- :tacn:`under` tactic that alleviates this bookkeeping:
+ The :tacn:`under` tactic lets one perform the same operation in a more
+ convenient way:
.. coqtop:: all abort
- Lemma example_G (n : nat) : G (fun n => n - n) n >= 0.
- under eq_G => m do rewrite subnn.
+ Lemma example_map l : sumlist (map (fun m => m - m) l) = 0.
+ under eq_map => m do rewrite subnn.
+
+
+The under tactic
+````````````````
+
+The convenience :tacn:`under` tactic supports the following syntax:
-.. tacn:: under {? @occ_switch} {? [ @r_pattern ] } @term {? => @i_item} {? do ( @tactic | [ {*| @tactic } ] ) }
+.. tacn:: under {? @r_prefix } @term {? => {+ @i_item}} {? do ( @tactic | [ {*| @tactic } ] ) }
:name: under
- Rewrite under :token:`term`. We distinguish two modes,
- :ref:`interactive mode <under_interactive>` without a ``do`` clause, and
- :ref:`one-liner mode <under_one_liner>` with a ``do`` clause,
- which are explained in more detail below.
+ Operate under the context proved to be extensional by
+ lemma :token:`term`.
- .. exn:: Incorrect number of tactics (expected N tactics, was given N').
+ .. exn:: Incorrect number of tactics (expected N tactics, was given M).
This error can occur when using the version with a ``do`` clause.
+ The multiplier part of :token:`r_prefix` is not supported.
+
+We distinguish two modes,
+:ref:`interactive mode <under_interactive>` without a ``do`` clause, and
+:ref:`one-liner mode <under_one_liner>` with a ``do`` clause,
+which are explained in more detail below.
+
.. _under_interactive:
Interactive mode
````````````````
+
+Let us redo the running example in interactive mode.
+
+.. example::
+
+ .. coqtop:: all abort
+
+ Lemma example_map l : sumlist (map (fun m => m - m) l) = 0.
+ under eq_map => m.
+ rewrite subnn.
+ by over.
+
The execution of the Ltac expression:
-:n:`under {@occ_switch}[@r_pattern]@term => [i|j].`
+
+:n:`under @term => [ @i_item__1 | … | @i_item__n ].`
+
involves the following steps:
-1. It performs a full-blown :n:`rewrite {@occ_switch}[@r_pattern]@term`,
- without failing like in the above example with ``eq_G``, but
- creating evars.
+1. It performs a :n:`rewrite @term`
+ without failing like in the above example with ``eq_map``, but
+ creating evars (see :tacn:`evar`). If :n:`term` is prefixed by
+ a pattern or an occurrence selector, then the modifiers is honoured.
+
+2. As a n-branches intro pattern is provided :tacn:`under` checks that
+ n+1 subgoals have been created. The last one is the main subgoal,
+ while the other ones correspond to premises of the rewrite rule (such as
+ ``forall n, F1 n = F2 n`` for ``eq_map``).
+
+3. Then :tacn:`under` checks that the first n subgoals
+ are (quantified) equalities or double implications between a
+ term and an evar (e.g. ``m - m = ?F2 m`` in the running example).
-2. As a 2-branches intro pattern is provided, it checks that 3 subgoals
- have been created (the last one being the initial, main subgoal that
- now contains some evars).
+4. If so :tacn:`under` executes the corresponding
+ intro pattern :n:`@ipat__i` in each goal.
-3. In the first 2 subgoals, it introduces ``i``, resp. ``j``, and tests
- whether these 2 subgoals are (quantified) equalities involving
- an evar. For example: ``some_expr(i) = ?Goal i``.
+5. Then :tacn:`under` protects these n goals against an
+ accidental instantiation of the evar. These protected goals are displayed
+ using the ``Under[ … ]`` notation (e.g. ``Under[ m - m ]``
+ in the running example).
-4. If this is the case, the subgoal is manipulated to get the
- provably equivalent goal ``Under _ (some_expr i) (?Goal i)``,
- pretty-printed as ``'Under[ some_expr i ]``.
- This change avoids instantiating the evar by accident (e.g., if one
- uses a proof script such as: :n:`rewrite lem1 // lem2`).
+6. The expression inside the ``Under[ … ]`` notation can be
+ proved equivalent to the desired expression
+ by using a regular tacn:`rewrite` tactic.
-5. Finally, a post-processing step is performed in the main (here, 3rd)
- subgoal to try keeping (in the object after the rewrite) the same
- bound variables as the identifers provided in the first
- intro pattern branch (here, ``i``).
+7. Interactive editing of the first n goals has to be signalled by
+ using the :tacn:`over` tactic or rewrite rule (see below).
-Note:
+8. Finally, a post-processing step is performed in the main goal
+ to keep the name(s) for the bound variables chosen by the user in
+ the intro pattern for the first branch.
-+ For the steps 3. and 4. above, the :tacn:`under` tactic also
- supports subgoals of the form ``some_expr(i) <-> ?Goal i``, which
- are "protected" in the form ``Under_iff (some_expr i) (?Goal i)``,
- itself pretty-printed as ``'Under_iff[ some_expr i ]``.
+.. _over_ssr:
+
+The over tactic
++++++++++++++++
Two equivalent facilities (a terminator and a lemma) are provided to
-close intermediate subgoal ``Under[ … ]`` and instantiate the
-corresponding evar:
+close intermediate subgoals generated by :tacn:`under` (i.e. goals
+displayed as ``Under[ … ]``):
.. tacn:: over
:name: over
@@ -3764,27 +3799,18 @@ corresponding evar:
This is a variant of :tacn:`over` in order to close ``Under_iff[ … ]``
goals, relying on the ``over_iff`` lemma.
-Let us focus again on the simple example involving an extensionality
-lemma ``eq_G``, but this time in interactive mode:
-
-.. example::
-
- .. coqtop:: all abort
-
- Lemma example_G (n : nat) : G (fun n => n - n) n >= 0.
- under eq_G => m.
- rewrite subnn.
- over.
-
.. _under_one_liner:
One-liner mode
``````````````
The Ltac expression:
-:n:`under {@occ_switch}[@r_pattern]@term => [i|j] do [tac1|tac2]`
-can be viewed as a shorter form for the following Ltac expression:
-:n:`under {@occ_switch}[@r_pattern]@term => [i|j|]; [tac1; over | tac2; over | cbv beta iota].`
+
+:n:`under @term => [ @i_item__1 | … | @i_item__n ] do [ @tac__1 | … | @tac__n ].`
+
+can be seen as a shorter form for the following expression:
+
+:n:`under @term => [ @i_item__1 | … | @i_item__n | ]; [ @tac__1; over | … | @tac__n; over | cbv beta iota ].`
Notes:
@@ -3792,19 +3818,19 @@ Notes:
redexes that could be introduced after the substitution of the evars
by the :tacn:`under` tactic.
-+ Note that the provided tactics (e.g., ``tac1``) can just as well
++ Note that the provided tactics can as well
involve other :tacn:`under` tactics. See below for a typical example
involving the `bigop` theory from the Mathematical Components library.
-+ If there is only one tactic, the brackets are optional, i.e.:
- :n:`under @term => [i] do [tac1]` is equivalent to:
- :n:`under @term => i do tac1.` and that shorter form should be
++ If there is only one tactic, the brackets can be omitted, e.g.:
+ :n:`under @term => i do @tac.` and that shorter form should be
preferred.
-+ If the ``do`` clause is provided but the intro pattern is omitted,
- the Ltac expression:
- :n:`under {@occ_switch}[@r_pattern]@term do [tac1|tac2]` defaults to:
- :n:`under {@occ_switch}[@r_pattern]@term => [*|*] do [tac1|tac2].`
++ If the ``do`` clause is provided and the intro pattern is omitted,
+ then the defeault :token:`i_item` ``*`` is applied to each branch.
+ Eg the Ltac expression
+ :n:`under @term do [ @tac__1 | … | @tac__n ]` is equivalent to
+ :n:`under @term => [ * | … | * ] do [ @tac__1 | … | @tac__n ].`
.. example::
@@ -3815,27 +3841,25 @@ Notes:
Unset Strict Implicit.
Unset Printing Implicit Defensive.
+ Coercion is_true : bool >-> Sortclass.
+
Reserved Notation "\big [ op / idx ]_ ( m <= i < n | P ) F"
(at level 36, F at level 36, op, idx at level 10, m, i, n at level 50,
format "'[' \big [ op / idx ]_ ( m <= i < n | P ) F ']'").
- Reserved Notation "\big [ op / idx ]_ ( m <= i < n ) F"
- (at level 36, F at level 36, op, idx at level 10, i, m, n at level 50,
- format "'[' \big [ op / idx ]_ ( m <= i < n ) '/ ' F ']'").
-
Variant bigbody (R I : Type) : Type :=
BigBody : forall (_ : I) (_ : forall (_ : R) (_ : R), R) (_ : bool) (_ : R), bigbody R I.
Parameter bigop :
forall (R I : Type) (_ : R) (_ : list I) (_ : forall _ : I, bigbody R I), R.
- Axiom eq_bigr :
+ Axiom eq_bigr_ :
forall (R : Type) (idx : R) (op : forall (_ : R) (_ : R), R) (I : Type)
(r : list I) (P : I -> bool) (F1 F2 : I -> R),
- (forall x : I, P x = true -> F1 x = F2 x) ->
+ (forall x : I, is_true (P x) -> F1 x = F2 x) ->
bigop idx r (fun i : I => BigBody i op (P i) (F1 i)) =
bigop idx r (fun i : I => BigBody i op (P i) (F2 i)).
- Axiom eq_big :
+ Axiom eq_big_ :
forall (R : Type) (idx : R) (op : R -> R -> R) (I : Type) (r : list I)
(P1 P2 : I -> bool) (F1 F2 : I -> R),
(forall x : I, P1 x = P2 x) ->
@@ -3846,34 +3870,40 @@ Notes:
Reserved Notation "\sum_ ( m <= i < n | P ) F"
(at level 41, F at level 41, i, m, n at level 50,
format "'[' \sum_ ( m <= i < n | P ) '/ ' F ']'").
- Reserved Notation "\sum_ ( m <= i < n ) F"
- (at level 41, F at level 41, i, m, n at level 50,
- format "'[' \sum_ ( m <= i < n ) '/ ' F ']'").
Parameter index_iota : nat -> nat -> list nat.
Notation "\big [ op / idx ]_ ( m <= i < n | P ) F" :=
(bigop idx (index_iota m n) (fun i : nat => BigBody i op P%bool F)).
- Notation "\big [ op / idx ]_ ( m <= i < n ) F" :=
- (bigop idx (index_iota m n) (fun i : nat => BigBody i op true F)).
Notation "\sum_ ( m <= i < n | P ) F" :=
(\big[plus/O]_(m <= i < n | P%bool) F%nat).
- Notation "\sum_ ( m <= i < n ) F" :=
- (\big[plus/O]_(m <= i < n) F%nat).
+
+ Notation eq_bigr := (fun n m => eq_bigr_ 0 plus (index_iota n m)).
+ Notation eq_big := (fun n m => eq_big_ 0 plus (index_iota n m)).
+
+ Parameter odd : nat -> bool.
+ Parameter prime : nat -> bool.
.. coqtop:: in
Parameter addnC : forall m n : nat, m + n = n + m.
Parameter muln1 : forall n : nat, n * 1 = n.
- Parameter odd : nat -> bool.
.. coqtop:: all
- Lemma test_big_nested (F G : nat -> nat) (m n : nat) :
- \sum_(0 <= a < m) \sum_(0 <= j < n | odd (j * 1)) (a + j) =
- \sum_(0 <= i < m) \sum_(0 <= j < n | odd j) (j + i).
- under eq_bigr => i Hi do under eq_big => [j|j Hj] do [rewrite muln1|rewrite addnC].
+ Check eq_bigr.
+ Check eq_big.
+
+ Lemma test_big_nested (m n : nat) :
+ \sum_(0 <= a < m | prime a) \sum_(0 <= j < n | odd (j * 1)) (a + j) =
+ \sum_(0 <= i < m | prime i) \sum_(0 <= j < n | odd j) (j + i).
+ under eq_bigr => i prime_i do
+ under eq_big => [ j | j odd_j ] do
+ [ rewrite (muln1 j) | rewrite (addnC i j) ].
+
+ Remark how the final goal uses the name ``i`` (the name given in the
+ intro pattern) rather than ``a`` in the binder of the first summation.
.. _locking_ssr:
@@ -5612,13 +5642,13 @@ respectively.
rewrite (see :ref:`rewriting_ssr`)
-.. tacn:: under {? @occ_switch} {? [ @r_pattern ] } @term {? => @i_item} {? do ( @tactic | [ {*| @tactic } ] )}
+.. tacn:: under {? @r_prefix } @term {? => {+ @i_item}} {? do ( @tactic | [ {*| @tactic } ] )}
- under (see :ref:`under_over_ssr`)
+ under (see :ref:`under_ssr`)
.. tacn:: over
- over (see :ref:`under_over_ssr`)
+ over (see :ref:`over_ssr`)
.. tacn:: have {* @i_item } {? @i_pattern } {? @s_item %| {+ @ssr_binder } } {? : @term } := @term
have {* @i_item } {? @i_pattern } {? @s_item %| {+ @ssr_binder } } : @term {? by @tactic }