Tricks in Coq
Some tips, tricks, and features in Coq that are hard to discover.
If you have a trick you've found useful feel free to submit an issue or pull request!
Ltac
patterntacticlazymatchfor better error messagesdeextactic::=to re-define Ltaclearnapproach - see Clément's thesisunshelvetactical, especially useful with an eapply - good example use case is constructing an object by refinement where the obligations end up being your proofs with the values as evars, when you wanted to construct the values by proofunfold "+"worksdestruct matchestactic- using
instantiateto modify evar environment (thanks to Jonathan Leivent on coq-club) eexists ?[x]lets one name an existential variable to be able to refer to it later- strong induction is in the standard library:
Require Import Arith.and useinduction n as [n IHn] using lt_wf_ind. - induction on the length of a list:
Require Import Coq.Arith.Wf_nat.andinduction xs as [xs IHxs] using (induction_ltof1 _ (@length _)); unfold ltof in IHxs. debug auto,debug eauto, anddebug trivialgive traces, including failed invocations.info_auto,info_eauto, andinfo_trivialare less verbose ways to debug which only report what the resulting proof includesconstructorandeconstructorbacktrack over the constructors over an inductive, which lets you do fun things exploring the constructors of an inductive type. See Constructors.v for some demonstrations.- There's a way to destruct and maintain an equality:
destruct_with_eqn x. You can also dodestruct x eqn:Hto explicitly name the equality hypothesis. This is similar tocase_eq x; intros; I'm not sure what the practical differences are. rew H in tnotation to useeq_rectfor a (safe) "type cast". Need to importEqNotations- see RewNotation.v for a working example.intro-patterns can be combined in a non-trivial way:intros [=->%lemma]-- see IntroPatterns.v.changetactic supports patterns (?var): e.g.repeat change (fun x => ?f x) with fwould eta-reduce all the functions (of arbitrary arity) in the goal.- One way to implement a tactic that sleeps for n seconds is in Sleep.v.
- Some tactics take an "occurrence clause" to select where they apply. The common ones are
in *andin Hto apply everywhere and in a specific hypotheses, but there are actually a bunch of forms. The syntax is really silly so I'm just going to give examples and hope they help.in H1, H2(justH1andH2)in H1, H2 |- *(H1,H2, and the goal)in * |-(just hypotheses)in |-(nowhere)in |- *(just the goal, same as leaving the whole thing off)in * |- *(everywhere, same asin *) Why have all these forms, particularly the last two? They're useless, but if Coq ever supported first-class occurrence sets (that is, you could write tactics that takes occurrence sets and operate on them) they might be handy. Exceptin |-, which is probably useless and just an artifact of the grammar.
- You can use notations to shorten repetitive Ltac patterns (much like Haskell's PatternSynonyms). Define a notation with holes (underscores) and use it in an Ltac match, eg
Notation anyplus := (_ + _).and then
I would recommend usingmatch goal with | |- context[anyplus] => idtac endLocal Notationso the notation isn't available outside the current file. - You can make all constructors of an inductive hints with
Hint Constructors; you can also do this locally in a proof witheauto using twheretis the name of the inductive. - The
intuitiontactic has some unexpected behaviors. It takes a tactic to run on each goal, which isauto with *by default, using hints from all hint databases.intuition idtacorintuition eautoare both much safer. When using these, note thatintuition eauto; simplis parsed asintuition (eauto; simpl), which is unlikely to be what you want; you'll need to instead write(intuition eauto); simpl.
Gallina
-
tactics in terms, eg
ltac:(eauto)can provide a proof argument -
maximally inserted implicit arguments are implicit even when for identifier alone (eg,
nilis defined to include the implicit list element type) -
maximally inserted arguments can be defined differently for different numbers of arguments - undocumented but
eq_reflprovides an example -
r.(Field)syntax: same asField r, but convenient whenFieldis a projection function for the (record) type ofr. If you use these, you might also wantSet Printing Projectionsso Coq re-prints calls to projections with the same syntax. -
Functionvernacular provides a more advanced way to define recursive functions, which removes the restriction of having a structurally decreasing argument; you just need to specify a well-founded relation or a decreasing measure maps to a nat, then prove all necessary obligations to show this function can terminate. See manual and examples inFunction.vfor more details.Two alternatives are considerable as drop-in replacements for
Function.Program Fixpointmay be useful when defining a nested recursive function. See manual and this StackOverflow post.- CPDT's way of defining general recursive functions with
Fixcombinator.
-
One can pattern-match on tuples under lambdas:
Definition fst {A B} : (A * B) -> A := fun '(x,_) => x.(works since Coq 8.6). -
Records fields can be defined with
:>, which make that field accessor a coercion. There are three ways to use this (since there are three types of coercion classes). See Coercions.v for some concrete examples.- if the field is an ordinary type, the record can be used as that type (the field will implicitly be accessed)
- if the field has a function type, the record can be called
- if the field is a sort (eg,
Type), then the record can be used as a type
-
The type classes in RelationClasses are useful but can be repetitive to prove. RelationInstances.v goes through a few ways of making these more convenient, and why you would want to do so (basically you can make
reflexivity,transitivity, andsymmetrymore powerful). -
The types of inductives can be definitions, as long as they expand to an "arity" (a function type ending in
Prop,Set, orType). See ArityDefinition.v.
Other Coq commands
Searchvernacular variants; see Search.v for examples.Search s -Learntfor a search of local hypotheses excluding LearntLocatecan search for notation, including partial searches.Optimize Heap(undocumented) runs GC (specificallyGc.compact)Optimize Proof(undocumented) runs several simplifications on the current proof term (seeProofview.compact)Generalizable Variable Aenables implicit generalization;Definition id(x:A) := xwill implicitly add a parameterAbeforex.Generalizable All Variables` enables implicit generalization for any identifier. Note that this surprisingly allows generalization without a backtick in Instances; see InstanceGeneralization.v. Issue #6030 generously requests this behavior be documented, but it should probably require enabling some option.Checksupports partial terms, printing a type along with a context of evars. A cool example isCheck (id _ _), where the first underscore must be a function (along with other constraints on the types involved).
Using Coq
- You can pass
-noinittocoqcorcoqtopto avoid loading the standard library. Ltac is provided as a plugin loaded by the standard library; to load it you needDeclare ML Module "ltac_plugin".(see NoInit.v).