1008 lines
34 KiB
Coq
1008 lines
34 KiB
Coq
(** * Lecture 4: Tactics in UniMath *)
|
||
(** based on material prepared by Ralph Matthes *)
|
||
|
||
(** This is the extended version of a presentation at the
|
||
School on Univalent Mathematics 2024 in Cortona, meant for self-study
|
||
and for exploring the UniMath library.
|
||
*)
|
||
|
||
|
||
(** Compiles with the command
|
||
[[
|
||
coqc -type-in-type tactics_lecture_extended.v
|
||
]]
|
||
when Coq is set up according to the instructions for this school and the associated coqc executable
|
||
has priority in the path. However, you do not need to compile this file. The option is crucial, and
|
||
also your own developments will need the Coq options configured through the installation instructions,
|
||
most notably the present one. *)
|
||
|
||
(** Can be transformed into HTML documentation with the command
|
||
[[
|
||
coqdoc -utf8 tactics_lecture_extended.v
|
||
]]
|
||
(If internal links in the generated lecture_tactics_long_version.html are desired,
|
||
compilation with coqc is needed.)
|
||
*)
|
||
|
||
(** In Coq, one can define concepts by directly giving well-typed
|
||
terms (see Part 2), but one can also be helped in the construction by the
|
||
interactive mode.
|
||
*)
|
||
|
||
Require Import UniMath.Foundations.Preamble.
|
||
(* Require Import UniMath.CategoryTheory.All. *)
|
||
|
||
(** ** define a concept interactively: *)
|
||
|
||
Locate bool. (** a separate definition - [Init.Datatypes.bool] is in the Coq library,
|
||
not available for UniMath *)
|
||
|
||
Definition myfirsttruthvalue: bool.
|
||
(** only the identifier and its type given, not the definiens *)
|
||
|
||
(** This opens the interactive mode.
|
||
|
||
The #<a href="https://github.com/UniMath/UniMath/tree/master/UniMath/README.md##unimath-coding-style">#UniMath
|
||
style guide#</a># asks us to start what follows with [Proof.] in a separate line.
|
||
In vanilla Coq, this would be optional (it is anyway a "nop"). *)
|
||
Proof.
|
||
(** Now we still have to give the term, but we are in interactive mode. *)
|
||
(** If you want to see everything in the currently loaded part of the UniMath library
|
||
that *involves* booleans, then do *)
|
||
Search bool.
|
||
(** If you only want to find library elements that *yield* booleans, then try *)
|
||
SearchPattern bool.
|
||
(** [true] does not take an argument, and it is already a term we can take as definiens. *)
|
||
exact true.
|
||
(** [exact] is a tactic which takes the term as argument and informs Coq in the proof mode to
|
||
finish the current goal with that term. *)
|
||
|
||
(** We see in the response buffer: "No more subgoals."
|
||
Hence, there is nothing more to do, except for leaving the proof mode properly. *)
|
||
Defined.
|
||
|
||
(** [Defined.] instructs Coq to complete the whole interactive construction of a term,
|
||
verify it and to associate it with the given identifer, here [myfirsttruthvalue].
|
||
This may go wrong for different reasons, including implementation errors of the Coq
|
||
system - that will not affect trustworthiness of the library. *)
|
||
Search bool.
|
||
(** The new definition appears at the beginning of the list. *)
|
||
Print myfirsttruthvalue. (** or just point to the identifier and hit the
|
||
key combination mentioned in Part 2 *)
|
||
|
||
(** [myfirsttruthvalue relies on an unsafe universe hierarchy] is output to indicate
|
||
that we are using Coq with option [-type-in-type]. *)
|
||
|
||
(** *** a more compelling example *)
|
||
Definition mysecondtruthvalue: bool.
|
||
Proof.
|
||
Search bool.
|
||
apply negb.
|
||
(** applies the function [negb] to obtain the required boolean,
|
||
thus the system has to ask for its argument *)
|
||
exact myfirsttruthvalue.
|
||
Defined.
|
||
|
||
Print mysecondtruthvalue.
|
||
(**
|
||
[[
|
||
mysecondtruthvalue = negb myfirsttruthvalue
|
||
: bool
|
||
]]
|
||
*)
|
||
|
||
(** the definition is "as is", evaluation can be done subsequently: *)
|
||
Eval compute in mysecondtruthvalue.
|
||
(**
|
||
[[
|
||
= false
|
||
: bool
|
||
]]
|
||
*)
|
||
|
||
(** Again, not much has been gained by the interactive mode. *)
|
||
|
||
(** Here, we see a copy of the definition from the Coq library: *)
|
||
Definition andb (b1 b2: bool) : bool := if b1 then b2 else false.
|
||
(** only for illustration purposes - it would be better to define
|
||
it according to UniMath style *)
|
||
|
||
Definition mythirdtruthvalue: bool.
|
||
Proof.
|
||
Search bool.
|
||
apply andb.
|
||
(** [apply andb.] applies the function [andb] to obtain the required boolean,
|
||
thus the system has to ask for its TWO arguments, one by one. *)
|
||
|
||
(** This follows the proof pattern of "backward chaining" that tries to
|
||
attack goals instead of building up evidence. In the course of action,
|
||
more goals can be generated. The proof effort is over when no more
|
||
goal remains. *)
|
||
|
||
(** UniMath coding style asks you to use proof structuring syntax,
|
||
while vanilla Coq would allow you to write formally verified
|
||
"spaghetti code". *)
|
||
|
||
(** We tell Coq that we start working on the first subgoal. *)
|
||
-
|
||
(** only the "focused" subgoal is now on display *)
|
||
apply andb.
|
||
(** this again spawns two subgoals *)
|
||
|
||
(** we tell Coq that we start working on the first subgoal *)
|
||
+
|
||
(** normally, one would not leave the "bullet symbol" isolated in a line *)
|
||
exact mysecondtruthvalue.
|
||
+ exact myfirsttruthvalue.
|
||
(** The response buffer signals:
|
||
[[
|
||
There are unfocused goals.
|
||
]]
|
||
ProofGeneral would give more precise instructions as how to proceed.
|
||
But we know what we are doing...
|
||
*)
|
||
- exact true.
|
||
Defined.
|
||
|
||
(** The usual "UniMath bullet order" is -, +, *, --, ++, **, ---, +++, ***,
|
||
and so on (all the ones shown are being used).
|
||
|
||
Coq does not impose any order, so one can start with, e.g., *****,
|
||
if need be for the sake of experimenting with a proof.
|
||
|
||
Reuse of bullets even on one branch is possible by enclosing subproofs
|
||
in curly braces {}.
|
||
*)
|
||
|
||
Print mythirdtruthvalue.
|
||
Eval compute in mythirdtruthvalue.
|
||
|
||
(** You only saw the tactics [exact] and [apply] at work, and there was no context. *)
|
||
|
||
(** ** doing Curry-Howard logic *)
|
||
|
||
(** Interactive mode is more wide-spread when it comes to carrying out proofs
|
||
(the command [Proof.] is reminiscent of that). *)
|
||
|
||
(** Disclaimer: this section has a logical flavour, but the "connectives"
|
||
are not confined to the world of propositional or predicate logic.
|
||
In particular, there is no reference to the sort Prop of Coq.
|
||
Prop is not used at all in UniMath!
|
||
|
||
On first reading, it is useful to focus on the logical meaning. *)
|
||
|
||
|
||
Locate "->". (** non-dependent product, can be seen as implication *)
|
||
Locate "∅".
|
||
Print empty. (** an inductive type that has no constructor *)
|
||
Locate "¬". (** we need to refer to the UniMath library more explicitly *)
|
||
|
||
Require Import UniMath.Foundations.PartA.
|
||
(** Do not write the import statements in the middle of a vernacular file.
|
||
Here, it is done to show the order of appearance, but this is only for
|
||
reasons of pedagogy.
|
||
*)
|
||
|
||
Locate "¬".
|
||
Print neg.
|
||
(** Negation is not a native concept; it is reduced to implication,
|
||
as is usual in constructive logic. *)
|
||
|
||
Locate "×".
|
||
Print dirprod. (** non-dependent sum, can be seen as conjunction *)
|
||
|
||
Definition combinatorS (A B C: UU): (A × B -> C) × (A -> B) × A -> C.
|
||
Proof.
|
||
(** how to infer an implication? *)
|
||
intro Hyp123.
|
||
set (Hyp1 := pr1 Hyp123).
|
||
(** This is already a bit of "forward chaining" which is a fact-building process. *)
|
||
set (Hyp23 := pr2 Hyp123).
|
||
cbn in Hyp23.
|
||
(** [cbn] simplifies a goal, and [cbn in H] does this for hypothesis [H];
|
||
note that [simpl] has the same high-level description but should better
|
||
be avoided in new developments. *)
|
||
set (Hyp2 := pr1 Hyp23).
|
||
set (Hyp3 := pr2 Hyp23).
|
||
cbn in Hyp3.
|
||
apply Hyp1.
|
||
apply tpair. (** could be done with [split.] as well *)
|
||
- assumption. (** instruct Coq to look into the current context *)
|
||
|
||
(** this could be done with [exact Hyp3.] as well *)
|
||
- apply Hyp2.
|
||
assumption.
|
||
Defined.
|
||
|
||
Print combinatorS.
|
||
Eval compute in combinatorS.
|
||
|
||
Local Definition combinatorS_intro_pattern (A B C: UU):
|
||
(A × B -> C) × (A -> B) × A -> C.
|
||
Proof.
|
||
intros [Hyp1 [Hyp2 Hyp3]]. (** deconstruct the hypothesis at the time of introduction;
|
||
notice that [×] associates to the right;
|
||
[intros] can also introduce multiple hypotheses, see below *)
|
||
apply Hyp1.
|
||
split.
|
||
- assumption.
|
||
- apply Hyp2.
|
||
assumption.
|
||
Defined.
|
||
|
||
Print combinatorS_intro_pattern.
|
||
|
||
(** the two definitions are even convertible: *)
|
||
Eval compute in combinatorS_intro_pattern.
|
||
|
||
Local Lemma combinatorS_intro_pattern_is_the_same:
|
||
combinatorS = combinatorS_intro_pattern.
|
||
Proof.
|
||
apply idpath.
|
||
Defined.
|
||
|
||
(** In late 2017, [combinatorS_intro_pattern] would have contained [match] constructs,
|
||
but now, the introduction patterns use less overhead when possible. The UniMath
|
||
style guide still does not want them to be used with square brackets. *)
|
||
|
||
(** another style to make life easier: *)
|
||
Local Definition combinatorS_destruct (A B C: UU):
|
||
(A × B -> C) × (A -> B) × A -> C.
|
||
Proof.
|
||
intro Hyp123.
|
||
destruct Hyp123 as [Hyp1 Hyp23]. (** deconstruct the hypothesis when needed *)
|
||
apply Hyp1.
|
||
destruct Hyp23 as [Hyp2 Hyp3]. (** deconstruct the hypothesis when needed *)
|
||
split.
|
||
- assumption.
|
||
- apply Hyp2.
|
||
assumption.
|
||
Defined.
|
||
|
||
Print combinatorS_destruct.
|
||
|
||
(** Again, the definition is definitionally equal to the first one: *)
|
||
Eval compute in combinatorS_destruct.
|
||
|
||
Local Lemma combinatorS_destruct_is_the_same: combinatorS = combinatorS_destruct.
|
||
Proof.
|
||
apply idpath.
|
||
Defined.
|
||
|
||
(** In late 2017, [combinatorS_destruct] would also have contained [match] constructs,
|
||
which is why [destruct] is forbidden in the UniMath style guide. Now, this is fine
|
||
in our example. *)
|
||
|
||
(** The (hitherto) preferred idiom: *)
|
||
Definition combinatorS_induction (A B C: UU): (A × B -> C) × (A -> B) × A -> C.
|
||
Proof.
|
||
intro Hyp123.
|
||
induction Hyp123 as [Hyp1 Hyp23].
|
||
apply Hyp1.
|
||
induction Hyp23 as [Hyp2 Hyp3].
|
||
split.
|
||
- assumption.
|
||
- apply Hyp2.
|
||
assumption.
|
||
Defined.
|
||
|
||
Print combinatorS_induction.
|
||
Eval compute in combinatorS_induction.
|
||
(** the comfort for the user does not change the normal form of the constructed proof *)
|
||
|
||
Definition combinatorS_curried (A B C: UU): (A -> B -> C) -> (A -> B) -> A -> C.
|
||
Proof.
|
||
(** use [intro] three times or rather [intros] once; reasonable coding style
|
||
gives names to all hypotheses that are not already present
|
||
in the goal formula, see also the next definition *)
|
||
intros H1 H2 H3.
|
||
apply H1.
|
||
- assumption.
|
||
- set (proofofB := H2 H3).
|
||
(** set up abbreviations that can make use of the current context;
|
||
will be considered as an extra element of the context: *)
|
||
assumption.
|
||
Defined.
|
||
|
||
Print combinatorS_curried.
|
||
(** We see that [set] gives rise to [let]-expressions that are known
|
||
from functional programming languages, in other words: the use of
|
||
[set] is not a "macro" facility to ease typing. *)
|
||
|
||
(** [let]-bindings disappear when computing the normal form of a term: *)
|
||
Eval compute in combinatorS_curried.
|
||
|
||
(** [set] can only be used if the term of the desired type is provided,
|
||
but we can also work interactively as follows: *)
|
||
Definition combinatorS_curried_with_assert (A B C: UU):
|
||
(A -> B -> C) -> (A -> B) -> A -> C.
|
||
Proof.
|
||
intros H1 H2 H3.
|
||
(** we can momentarily forget about our goal and build up knowledge: *)
|
||
assert (proofofB : B).
|
||
(** the current goal [C] becomes the second sub-goal, and the new current goal is [B] *)
|
||
|
||
(** It is not wise to handle this situation by "bullets" since many assertions
|
||
can appear in a linearly thought argument. It would pretend a tree structure
|
||
although it would rather be a comb. The proof of the assertion should
|
||
be packaged by enclosing it in curly braces like so: *)
|
||
{ apply H2.
|
||
assumption.
|
||
}
|
||
(** Now, [proofofB] is in the context with type [B]. *)
|
||
apply H1.
|
||
- assumption.
|
||
- assumption.
|
||
Defined.
|
||
|
||
(** the wildcard [?] for [intros] *)
|
||
Definition combinatorS_curried_variant (A B C: UU):
|
||
(A -> B -> C) -> (A -> B) -> forall H7: A, C.
|
||
Proof.
|
||
intros H1 H2 ?.
|
||
(** a question mark instructs Coq to use the corresponding identifier
|
||
from the goal formula *)
|
||
exact (H1 H7 (H2 H7)).
|
||
Defined.
|
||
(** the wildcard [_] for [intros] forgets the respective hypothesis *)
|
||
|
||
Locate "⨿". (** this symbol is typed as \amalg when the recommended extension
|
||
packages for VSCode are loaded *)
|
||
Print coprod. (** defined in UniMath preamble as inductive type,
|
||
can be seen as disjunction *)
|
||
|
||
Locate "∏".
|
||
|
||
Locate "=". (** the identity type of UniMath *)
|
||
Print paths.
|
||
|
||
(** A word of warning for those who read "Coq in a Hurry": [SearchRewrite]
|
||
does not find equations w.r.t. this notion, only w.r.t. Coq's built-in
|
||
propositional equality. *)
|
||
SearchPattern (paths _ _).
|
||
(** Among the search results is [pathsinv0r] that has [idpath] in its conclusion. *)
|
||
SearchRewrite idpath.
|
||
(** No result! *)
|
||
|
||
(** *** How to decompose formulas *)
|
||
|
||
(** In "Coq in a Hurry", Yves Bertot gives recipes for decomposing the usual logical
|
||
connectives. Crucially, one has to distinguish between decomposition of the goal
|
||
or decomposition of a hypothesis in the context.
|
||
|
||
Here, we do it alike.
|
||
*)
|
||
|
||
(** **** Decomposition of goal formulas:
|
||
|
||
A1,...,An -> B: tactic [intro] or [intros]
|
||
|
||
[¬ A]: idem (negation is defined through implication)
|
||
|
||
Π-type: idem (implication is a special case of product)
|
||
|
||
[×]: [apply dirprodpair], less specifically [apply tpair] or [split]
|
||
|
||
Σ-type: [use tpair] or [exists] or [split with], see explanations below
|
||
|
||
[A ⨿ B]: [apply ii1] or [apply ii2], but this constitutes a choice
|
||
of which way to go
|
||
|
||
[A = B]: [apply idpath], however this only works when the expressions
|
||
are convertible
|
||
|
||
[nat]: [exact 1000], for example (a logical reading is not
|
||
useful for this type); beware that UniMath knows only 27 numerals,
|
||
[Goal nat. Fail exact 2022.] leads to
|
||
[[
|
||
The command has indeed failed with message: No interpretation for number "2022".
|
||
]]
|
||
*)
|
||
|
||
(** **** Decomposition of formula of hypothesis [H]:
|
||
|
||
[∅]: [induction H]
|
||
|
||
This terminates a goal. (It corresponds to ex falso quodlibet.)
|
||
|
||
There is naturally no recipe for getting rid of [∅] in the conclusion.
|
||
But [apply fromempty] allows to replace any goal by [∅].
|
||
|
||
A1,...,An -> B: [apply H], but the formula has to fit with the goal
|
||
|
||
|
||
[×]: [induction H as [H1 H2]]
|
||
|
||
As seen above, this introduces names of hypotheses for the two components.
|
||
|
||
Σ-type: idem, but rather more asymmetric as [induction H as [x H']]
|
||
|
||
[A ⨿ B]: [induction H as [H1 | H2]]
|
||
|
||
This introduces names for the hypotheses in the two branches.
|
||
|
||
[A = B]: [induction H]
|
||
|
||
The supposedly equal [A] and [B] become the same [A] in the goal.
|
||
|
||
This is the least intuitive rule for the non-expert in type theory.
|
||
|
||
[nat]: [induction n as [ | n IH]]
|
||
|
||
Here, we assume that the hypothesis has the name [n] which
|
||
is more idiomatic than [H], and there is no extra name in
|
||
the base case, while in the step case, the preceding number
|
||
is now given the name [n] and the induction hypothesis is
|
||
named [IH].
|
||
*)
|
||
|
||
(** ** Handling unfinished proofs *)
|
||
|
||
(** In the middle of a proof effort - not in the UniMath library - you can use
|
||
[admit] to abandon the current goal. *)
|
||
Local Lemma badex1 (A: UU): ∅ × (A -> A).
|
||
Proof.
|
||
split.
|
||
- (** seems difficult in the current context *)
|
||
admit.
|
||
|
||
(** we continue with decent proof work: *)
|
||
- intro H.
|
||
assumption.
|
||
Admitted.
|
||
|
||
(** This is strictly forbidden to commit to UniMath! [admit] allows to pursue the other goals,
|
||
while [Admitted.] makes the lemma available for further proofs. A warning is shown that
|
||
[badex1] has been assumed as axiom. *)
|
||
|
||
(** An alternative to interrupt work on a proof: *)
|
||
Lemma badex2 (A: UU): ∅ × (A -> A).
|
||
Proof.
|
||
split.
|
||
-
|
||
Abort.
|
||
(** [badex2] is not in the symbol table. *)
|
||
|
||
(** [Abort.] is a way of documenting a problem with proving a result.
|
||
At least, Coq can check the partial proof up to the [Abort.] command. *)
|
||
|
||
(** ** Working with holes in proofs *)
|
||
|
||
(** Our previous proofs were particularly clear because the goal formulas
|
||
and all hypotheses were fully given by the system.
|
||
*)
|
||
|
||
Print pathscomp0.
|
||
(** This is the UniMath proof of transitivity of equality. *)
|
||
|
||
(** The salient feature of transitivity is that the intermediate
|
||
expression cannot be deduced from the equation to be proven. *)
|
||
Lemma badex3 (A B C D: UU) : ((A × B) × (C × D)) = (A × (B × C) × D).
|
||
(** Notice that the outermost parentheses are needed here. *)
|
||
Proof.
|
||
Fail apply pathscomp0.
|
||
(**
|
||
[[
|
||
The command has indeed failed with message:
|
||
Cannot infer the implicit parameter b of pathscomp0 whose type is
|
||
"Type" in environment:
|
||
A, B, C, D : UU
|
||
]]
|
||
|
||
[Fail] announces failure and therefore allows to continue with
|
||
the interpretation of the vernacular file.
|
||
|
||
We need to help Coq with the argument [b] to [pathscomp0].
|
||
*)
|
||
apply (pathscomp0 (b := A × (B × (C × D)))).
|
||
- (** is this not just associativity with third argument [C × D]? *)
|
||
SearchPattern (_ × _ = _ × _).
|
||
(** Nothing for our equation - we can only hope for weak equivalence ≃. *)
|
||
Abort.
|
||
|
||
SearchPattern(_ ≃ _).
|
||
Print weqcomp.
|
||
Print weqdirprodasstor.
|
||
Print weqdirprodasstol.
|
||
Print weqdirprodf.
|
||
Print idweq.
|
||
|
||
Lemma assocex (A B C D: UU) : ((A × B) × (C × D)) ≃ (A × (B × C) × D).
|
||
Proof.
|
||
Fail apply weqcomp.
|
||
eapply weqcomp.
|
||
(** [eapply] generates "existential variables" for the expressions
|
||
it cannot infer from applying a lemma.
|
||
|
||
The further proof will narrow on those variables and finally
|
||
make them disappear - otherwise, the proof is not considered
|
||
completed.
|
||
*)
|
||
- (** We recall that on this side, only associativity was missing. *)
|
||
apply weqdirprodasstor.
|
||
- (** The subgoal is now fully given. *)
|
||
|
||
(** The missing link is associativity, but only on the
|
||
right-hand side of the top [×] symbol. *)
|
||
apply weqdirprodf.
|
||
+ apply idweq.
|
||
+ apply weqdirprodasstol.
|
||
Defined.
|
||
|
||
(** Warning: tactic [exact] does not work if there are existential
|
||
variables in the goal, but [eexact] can then be tried. *)
|
||
|
||
Lemma sumex (A: UU) (P Q: A -> UU):
|
||
(∑ x: A, P x × Q x) -> (∑ x: A, P x) × ∑ x:A, Q x.
|
||
Proof.
|
||
(** decompose the implication: *)
|
||
intro H.
|
||
(** decompose the Σ-type: *)
|
||
induction H as [x H'].
|
||
(** decompose the pair: *)
|
||
induction H' as [H1 H2].
|
||
(** decompose the pair in the goal *)
|
||
split.
|
||
- Fail split.
|
||
(**
|
||
[[
|
||
The command has indeed failed with message:
|
||
Unable to find an instance for the variable pr1.
|
||
]]
|
||
*)
|
||
Fail (apply tpair).
|
||
(** A simple way out, by providing the first component: *)
|
||
split with x. (** [exists x] does the same *)
|
||
assumption.
|
||
- (** or use [eapply] and create an existential variable: *)
|
||
eapply tpair.
|
||
Fail assumption. (** the assumption [H2] does not agree with the goal *)
|
||
eexact H2.
|
||
Defined.
|
||
(** Notice that [eapply tpair] is not used in the UniMath library,
|
||
since [use tpair] normally comes in handier, see below. *)
|
||
|
||
(** *** Warning on existential variables *)
|
||
(** It may happen that the process of instantiating existential variables
|
||
is not completed when all goals have been treated.
|
||
*)
|
||
|
||
(** an example adapted from one by Arnaud Spiwack, ~2007 *)
|
||
|
||
About unit. (** from the UniMath preamble *)
|
||
|
||
Local Definition P (x:nat) := unit.
|
||
|
||
Lemma uninstex: unit.
|
||
Proof.
|
||
refine ((fun x:P _ => _) _).
|
||
(** [refine] is like [exact], but one can leave holes with the wildcard "_".
|
||
This tactic should hardly be needed since most uses in UniMath
|
||
can be replaced by a use of the "tactic" [use], see further down
|
||
on this tactic notation for an Ltac definition.
|
||
|
||
Still, [refine] can come to rescue in difficult situations,
|
||
in particular during proof development. Its simpler variant
|
||
[simple refine] is captured by the [use] "tactic".
|
||
*)
|
||
- exact tt.
|
||
- exact tt.
|
||
(** Now, Coq presents a subgoal that pops up from the "shelved goals".
|
||
|
||
Still, no more "-" bullets can be used.
|
||
|
||
[[ -
|
||
Error: Wrong bullet - : No more subgoals.
|
||
]]
|
||
*)
|
||
|
||
Show Existentials.
|
||
(** a natural number is still being asked for *)
|
||
Unshelve.
|
||
(** Like this, we can focus on the remaining goal. *)
|
||
exact 0.
|
||
Defined.
|
||
|
||
(** one can also name the existential variables in [refine]: *)
|
||
Lemma uninstexnamed: unit.
|
||
Proof.
|
||
refine ((fun x:P ?[n] => _) _). (** give a name to the existential variable *)
|
||
- exact tt.
|
||
- exact tt.
|
||
Show Existentials.
|
||
Unshelve.
|
||
instantiate (n := 0). (** more symbols to type but better to grasp *)
|
||
Defined.
|
||
|
||
(** ** a bit more on equational reasoning *)
|
||
|
||
Section homot.
|
||
(** A section allows to introduce local variables/parameters
|
||
that will be bound outside of the section. *)
|
||
|
||
Locate "~".
|
||
(** printing ~ #~# *)
|
||
|
||
Print homot. (** this is just pointwise equality *)
|
||
Print idfun. (** the identity function *)
|
||
Locate "∘". (** exchanges the arguments of [funcomp] *)
|
||
Print funcomp.
|
||
(** plain function composition in diagrammatic order, i.e.,
|
||
first the first argument, then the second argument;
|
||
the second argument may even have a dependent type *)
|
||
|
||
Context (A B: UU).
|
||
(** makes good sense in a section, can be put in curly braces to indicate
|
||
they will be implicit arguments for every construction in the section *)
|
||
|
||
Definition interestingstatement : UU :=
|
||
∏ (v w : A → B) (v' w' : B → A),
|
||
w ∘ w' ~ idfun B → v' ∘ v ~ idfun A → v' ~ w' → v ~ w.
|
||
|
||
Check (isinjinvmap': interestingstatement).
|
||
|
||
Lemma ourisinjinvmap': interestingstatement.
|
||
Proof.
|
||
intros. (** is a nop since the formula structure is not analyzed *)
|
||
unfold interestingstatement. (** [unfold] unfolds a definition *)
|
||
intros ? ? ? ? homoth1 homoth2 hyp a.
|
||
(** the extra element [a] triggers Coq to unfold the formula further;
|
||
[unfold interestingstatement] was there only for illustration! *)
|
||
|
||
(** we want to use transitivity that is expressed by [pathscomp0] and
|
||
instruct Coq to take a specific intermediate term *)
|
||
Print Ltac intermediate_path. (** not telling because implicit arg. is not shown *)
|
||
intermediate_path (w (w' (v a))).
|
||
- apply pathsinv0. (** apply symmetry of equality *)
|
||
unfold homot in homoth1.
|
||
unfold funcomp in homoth1.
|
||
unfold idfun in homoth1.
|
||
apply homoth1. (** all the [unfold] were only for illustration! *)
|
||
-
|
||
Print maponpaths.
|
||
apply maponpaths.
|
||
unfold homot in hyp.
|
||
(** we use the equation in [hyp] from right to left, i.e., backwards: *)
|
||
rewrite <- hyp.
|
||
(** remark: for a forward rewrite, use [rewrite] without directional
|
||
argument *)
|
||
(** beautify the current goal: *)
|
||
change ((v' ∘ v) a = idfun A a).
|
||
(** just for illustration of [change] that allows to replace the goal
|
||
by a convertible expression; also works for hypotheses, e.g.: *)
|
||
change (v' ~ w') in hyp.
|
||
(** since [hyp] was no longer necessary, we should rather have deleted it: *)
|
||
clear hyp.
|
||
apply homoth2.
|
||
Defined.
|
||
|
||
Context (v w: A -> B) (v' w': B → A).
|
||
|
||
Eval compute in (ourisinjinvmap' v w v' w').
|
||
|
||
Opaque ourisinjinvmap'.
|
||
Eval compute in (ourisinjinvmap' v w v' w').
|
||
(** [Opaque] made the definition opaque in the sense that the identifier
|
||
is still in the symbol table, together with its type, but that it does
|
||
not evaluate to anything but itself.
|
||
|
||
If inhabitants of a type are irrelevant (for example if it is known
|
||
that there is at most one inhabitant, and if one therefore is not interested
|
||
in computing with that inhabitant), then opaqueness is an asset to make
|
||
the subsequent proof process lighter.
|
||
|
||
[Opaque] can be undone with [Transparent]:
|
||
*)
|
||
Transparent ourisinjinvmap'.
|
||
Eval compute in (ourisinjinvmap' v w v' w').
|
||
|
||
(** If one uses [Compute] in place of [Eval compute in], then [Opaque] has no effect. *)
|
||
|
||
(** Full and irreversible opaqueness is obtained for a construction
|
||
in interactive mode by completing it with [Qed.] in place of [Defined.]
|
||
|
||
Using [Qed.] is discouraged by the UniMath style guide. In Coq,
|
||
most lemmas, theorems, etc. (nearly every assertion in [Prop]) are
|
||
made opaque in this way. In UniMath, many lemmas enter subsequent
|
||
computation, and one should have good reasons for not closing an
|
||
interactive construction with [Defined.]. More than 5kloc of the UniMath
|
||
library have [Qed.], so these good reasons do exist and are not rare.
|
||
*)
|
||
|
||
End homot.
|
||
Check ourisinjinvmap'.
|
||
(** The section parameters [A] and [B] are abstracted away after the end
|
||
of the section - only the relevant ones. *)
|
||
|
||
(** [assert] is a "chameleon" w.r.t. to opaqueness: *)
|
||
Definition combinatorS_curried_with_assert2 (A B C: UU):
|
||
(A -> B -> C) -> (A -> B) -> A -> C.
|
||
Proof.
|
||
intros H1 H2 H3.
|
||
assert (proofofB : B).
|
||
{ apply H2.
|
||
assumption.
|
||
}
|
||
(** [proofofB] is just an identifier and not associated to the
|
||
construction we gave. Hence, the proof is opaque for us. *)
|
||
apply H1.
|
||
- assumption.
|
||
- assumption.
|
||
Defined.
|
||
Print combinatorS_curried_with_assert2.
|
||
(** We see that [proofofB] is there with its definition, so it is
|
||
transparent.
|
||
|
||
See much further below for [transparent assert] that is like
|
||
[assert], but consistently transparent.
|
||
*)
|
||
|
||
(** ** composing tactics *)
|
||
|
||
(** Up to now, we "composed" tactics in two ways: we gave them sequentially,
|
||
separated by periods, or we introduced a tree structure through the
|
||
"bullet" notation. We did not think of these operations as composition
|
||
of tactics, in particular since we had to trigger each of them separately
|
||
in interactive mode. However, we can also explicitly compose them, like so:
|
||
*)
|
||
Definition combinatorS_induction_in_one_step (A B C: UU):
|
||
(A × B -> C) × (A -> B) × A -> C.
|
||
Proof.
|
||
intro Hyp123;
|
||
induction Hyp123 as [Hyp1 Hyp23];
|
||
apply Hyp1;
|
||
induction Hyp23 as [Hyp2 Hyp3];
|
||
split;
|
||
[ assumption
|
||
| apply Hyp2;
|
||
assumption].
|
||
Defined.
|
||
|
||
(** The sequential composition is written by (infix) semicolon,
|
||
and the two branches reated by [split] are treated in the
|
||
|-separated list of arguments to the brackets. *)
|
||
|
||
(** Why would we want to do such compositions? There are at least four good reasons:
|
||
|
||
(1) We indicate that the intermediate results are irrelevant for someone who
|
||
executes the script so as to understand how and why the construction /
|
||
the proof works.
|
||
|
||
(2) The same tactic (expression) can uniformly treat all sub-goals stemming
|
||
from the preceding tactic application, as will be shown next.
|
||
*)
|
||
Definition combinatorS_curried_with_assert_in_one_step (A B C: UU):
|
||
(A -> B -> C) -> (A -> B) -> A -> C.
|
||
Proof.
|
||
intros H1 H2 H3;
|
||
assert (proofofB : B) by
|
||
( apply H2;
|
||
assumption
|
||
);
|
||
apply H1;
|
||
assumption.
|
||
Defined.
|
||
|
||
(** This illustrates the grouping of tactic expressions by parentheses, the variant
|
||
[assert by] of [assert] used when only one tactic expression forms the proof of
|
||
the assertion, and also point (2): the last line is simpler than the expected line
|
||
[[
|
||
[assumption | assumption].
|
||
]]
|
||
*)
|
||
|
||
(** Why would we want to do such compositions (cont'd)?
|
||
|
||
(3) We want to capture recurring patterns of construction / proof by tactics into
|
||
reusable Ltac definitions, see below.
|
||
|
||
(4) We want to make use of the [abstract] facility, explained now.
|
||
*)
|
||
|
||
Definition combinatorS_induction_with_abstract (A B C: UU):
|
||
(A × B -> C) × (A -> B) × A -> C.
|
||
Proof.
|
||
intro Hyp123;
|
||
induction Hyp123 as [Hyp1 Hyp23];
|
||
apply Hyp1;
|
||
induction Hyp23 as [Hyp2 Hyp3].
|
||
(** Now imagine that the following proof was very complicated but had no computational
|
||
relevance, i.e., could also be packed into a lemma whose proof would be finished
|
||
by [Qed]. We can encapsulate it into [abstract]: *)
|
||
abstract (split;
|
||
[ assumption
|
||
| apply Hyp2;
|
||
assumption]).
|
||
Defined.
|
||
|
||
Print combinatorS_induction_with_abstract.
|
||
(** The term features an occurrence of [combinatorS_induction_with_abstract_subproof]
|
||
that contains the abstracted part; using the latter name is forbidden by the
|
||
UniMath style guide. Note that [abstract] is used hundreds of times in the
|
||
UniMath library. *)
|
||
|
||
(** *** Ltac language for defining tactics *)
|
||
|
||
(** Disclaimer: Ltac can do more than that, in fact Ltac is the name of the
|
||
whole tactic language of Coq. *)
|
||
|
||
(** Ltac definitions can associate identifiers for tactics with tactic expressions.
|
||
|
||
We have already used one such identifier: [intermediate_path] in the [Foundations]
|
||
package of UniMath. In file [PartA.v], we have the code
|
||
[[
|
||
Ltac intermediate_path x := apply (pathscomp0 (b := x)).
|
||
]]
|
||
*)
|
||
Print Ltac intermediate_path.
|
||
(** does not show the formal argument [x] in the right-hand side.
|
||
Remedy (in ProofGeneral but not in VSCode): *)
|
||
Set Printing All.
|
||
Print Ltac intermediate_path.
|
||
Unset Printing All.
|
||
(** The problem with these Ltac definitions is that they are barely typed, they
|
||
behave rather like LaTeX macros. *)
|
||
Local Ltac intermediate_path_wrong x := apply (pathscomp0 (X := x)(b := x)).
|
||
(** This definition confounds the type argument [X] and its element [b].
|
||
The soundness of Coq is not at stake here, but the errors only appear
|
||
at runtime, as we will see below. Normal printing output hides the difference
|
||
with the correct tactic definition: *)
|
||
Print Ltac intermediate_path_wrong.
|
||
|
||
Section homot2.
|
||
Context (A B : UU).
|
||
|
||
Lemma ourisinjinvmap'_failed_proof: interestingstatement A B.
|
||
Proof.
|
||
intros ? ? ? ? homoth1 homoth2 hyp a.
|
||
Fail intermediate_path_wrong (w (w' (v a))).
|
||
(** The message does not point to the problem that argument [x] appears
|
||
a second time in the Ltac definition with a different needed type. *)
|
||
Abort.
|
||
End homot2.
|
||
(** See #<a href="https://github.com/UniMath/UniMath/blob/master/UniMath/PAdics/frac.v##L23">#[https://github.com/UniMath/UniMath/blob/master/UniMath/PAdics/frac.v#L23]#</a>#
|
||
for a huge Ltac definition in the UniMath library to appreciate the lack
|
||
of type information. *)
|
||
|
||
(** The UniMath library provides some Ltac definitions for general use: *)
|
||
Print Ltac etrans. (** no need to explain - rather an abbreviation *)
|
||
Set Printing All.
|
||
Print Ltac intermediate_weq. (** problem with VSCode analogous to [intermediate_path] *)
|
||
Unset Printing All.
|
||
|
||
(** for the next tactic *)
|
||
Require Import UniMath.MoreFoundations.Tactics.
|
||
|
||
Set Printing All.
|
||
Print Ltac show_id_type.
|
||
(** output with ProofGeneral (output with VSCode falls again short of crucial information)
|
||
[[
|
||
Ltac show_id_type :=
|
||
match goal with
|
||
| |- @paths ?ID _ _ => set (TYPE := ID); simpl in TYPE
|
||
end
|
||
]]
|
||
Hardly ever present in proofs in the library, but it can be an excellent tool
|
||
while trying to prove an equation: it puts the index of the path space
|
||
into the context. This index is invisible in the notation with an equals
|
||
sign that one normally sees as the goal, and coercions can easily give a wrong
|
||
impression about that index. *)
|
||
Unset Printing All.
|
||
|
||
(** **** The most useful Ltac definition of UniMath *)
|
||
Print Ltac simple_rapply.
|
||
(** It applies the [simple refine] tactic with zero up to fifteen unknown
|
||
arguments. *)
|
||
|
||
(** This tactic must not be used in UniMath since a "tactic notation"
|
||
is favoured: [Foundations/Preamble.v] contains the definition
|
||
[[
|
||
Tactic Notation "use" uconstr(p) := simple_rapply p.
|
||
]]
|
||
|
||
Use of [use]:
|
||
*)
|
||
Lemma sumex_with_use (A: UU) (P Q: A -> UU):
|
||
(∑ x:A, P x × Q x) -> (∑ x:A, P x) × ∑ x:A, Q x.
|
||
Proof.
|
||
intro H; induction H as [x H']; induction H' as [H1 H2].
|
||
split.
|
||
- use tpair.
|
||
+ assumption.
|
||
+ cbn. (** this is often necessary since [use] does as little as possible *)
|
||
assumption.
|
||
- (** to remind the version where the "witness" is given explicitly: *)
|
||
exists x; assumption.
|
||
Defined.
|
||
(** To conclude: [use tpair] is the right idiom for an interactive
|
||
construction of inhabitants of Σ-types. Note that the second
|
||
generated sub-goal may need [cbn] to make further tactics
|
||
applicable.
|
||
|
||
If the first component of the inhabitant is already at hand,
|
||
then the "exists" tactic yields a leaner proof script.
|
||
|
||
[use] is not confined to Σ-types. Whenever one would be
|
||
inclined to start trying to apply a lemma [H] with a varying
|
||
number of underscores, [use H] may be a better option.
|
||
*)
|
||
|
||
(** There is another recommendable tactic notation that is also by
|
||
Jason Gross:
|
||
[[
|
||
Tactic Notation "transparent" "assert"
|
||
"(" ident(name) ":" constr(type) ")" :=
|
||
simple refine (let name := (_ : type) in _).
|
||
]]
|
||
*)
|
||
Definition combinatorS_curried_with_transparent_assert (A B C: UU):
|
||
(A -> B -> C) -> (A -> B) -> A -> C.
|
||
Proof.
|
||
intros H1 H2 H3.
|
||
transparent assert (proofofB : B).
|
||
{ apply H2; assumption. } (** There is no [transparent assert by]. *)
|
||
|
||
(** Now, [proofB] is present with the constructed proof of [B]. *)
|
||
Abort.
|
||
(** To conclude: [transparent assert] is a replacement for [assert]
|
||
if the construction of the assertion is needed in the rest of
|
||
the proof.
|
||
*)
|
||
|
||
(** ** a final word, just on searching the library *)
|
||
|
||
(** [SearchPattern] searches for the given pattern in what the library
|
||
gives as *conclusions* of definitions, lemmas, etc., and the current
|
||
hypotheses.
|
||
|
||
[Search] searches in the (full) types of all the library elements (and
|
||
the current hypotheses). It may provide too many irrelevant result
|
||
for your question. At least, it will also show all the relevant ones.
|
||
|
||
Anyway, only the imported part of the library is searched. The quick
|
||
way for importing the whole UniMath library is
|
||
[[
|
||
Require Import UniMath.All.
|
||
]]
|
||
You may test it with
|
||
[[
|
||
SearchPattern (_ ≃ _).
|
||
]]
|
||
with very numerous results.
|
||
*)
|
||
|
||
(** ** List of tactics that were mentioned *)
|
||
(**
|
||
[[
|
||
exact
|
||
apply
|
||
intro
|
||
set
|
||
cbn / cbn in (old but sometimes useful form: simpl / simpl in)
|
||
assumption
|
||
intros (with pattern, with wild cards)
|
||
split / split with / exists
|
||
destruct as --- not desirable in UniMath
|
||
induction / induction as
|
||
admit --- only during proof development
|
||
eapply
|
||
eexact
|
||
refine --- first consider "use" instead
|
||
instantiate
|
||
unfold / unfold in
|
||
intermediate_path (Ltac def.)
|
||
rewrite / rewrite <-
|
||
change / change in
|
||
clear
|
||
assert {} / assert by
|
||
abstract
|
||
etrans (Ltac def.)
|
||
intermediate_weq (Ltac def.)
|
||
show_id_type (Ltac def.)
|
||
simple_rapply (Ltac def., not to be used)
|
||
use (Ltac notation)
|
||
transparent assert (Ltac notation)
|
||
]]
|
||
*)
|
||
|
||
(* End of file *)
|