forked from achlipala/frap
-
Notifications
You must be signed in to change notification settings - Fork 0
/
LogicProgramming.v
997 lines (776 loc) · 33 KB
/
LogicProgramming.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
(** Formal Reasoning About Programs <http://adam.chlipala.net/frap/>
* Supplementary Coq material: unification and logic programming
* Author: Adam Chlipala
* License: https://creativecommons.org/licenses/by-nc-nd/4.0/
* Much of the material comes from CPDT <http://adam.chlipala.net/cpdt/> by the same author. *)
Require Import Frap.
Set Implicit Arguments.
(** * Introducing Logic Programming *)
(* Recall the definition of addition from the standard library. *)
Definition real_plus := Eval compute in plus.
Print real_plus.
(* This is a recursive definition, in the style of functional programming. We
* might also follow the style of logic programming, which corresponds to the
* inductive relations we have defined in previous chapters. *)
Inductive plusR : nat -> nat -> nat -> Prop :=
| PlusO : forall m, plusR O m m
| PlusS : forall n m r, plusR n m r
-> plusR (S n) m (S r).
(* Intuitively, a fact [plusR n m r] only holds when [plus n m = r]. It is not
* hard to prove this correspondence formally. *)
Theorem plus_plusR : forall n m,
plusR n m (n + m).
Proof.
induct n; simplify.
constructor.
constructor.
apply IHn.
Qed.
(* We see here another instance of the very mechanical proof pattern that came
* up before: keep trying constructors and hypotheses. The tactic [auto] will
* automate searching through sequences of that kind, when we prime it with good
* suggestions of single proof steps to try, as with this command: *)
Local Hint Constructors plusR : core.
(* That is, every constructor of [plusR] should be considered as an atomic proof
* step, from which we enumerate step sequences. *)
Theorem plus_plusR_snazzy : forall n m,
plusR n m (n + m).
Proof.
induct n; simplify; auto.
Qed.
Theorem plusR_plus : forall n m r,
plusR n m r
-> r = n + m.
Proof.
induct 1; simplify; linear_arithmetic.
Qed.
(* With the functional definition of [plus], simple equalities about arithmetic
* follow by computation. *)
Example four_plus_three : 4 + 3 = 7.
Proof.
reflexivity.
Qed.
Print four_plus_three.
(* With the relational definition, the same equalities take more steps to prove,
* but the process is completely mechanical. For example, consider this
* simple-minded manual proof search strategy. The steps prefaced by [Fail] are
* intended to fail; they're included for explanatory value, to mimic a
* simple-minded try-everything strategy. *)
Example four_plus_three' : plusR 4 3 7.
Proof.
Fail apply PlusO.
apply PlusS.
Fail apply PlusO.
apply PlusS.
Fail apply PlusO.
apply PlusS.
Fail apply PlusO.
apply PlusS.
apply PlusO.
(* At this point the proof is completed. It is no doubt clear that a simple
* procedure could find all proofs of this kind for us. We are just exploring
* all possible proof trees, built from the two candidate steps [apply PlusO]
* and [apply PlusS]. Thus, [auto] is another great match! *)
Restart.
auto.
Qed.
Print four_plus_three'.
(* Let us try the same approach on a slightly more complex goal. *)
Example five_plus_three : plusR 5 3 8.
Proof.
auto.
(* This time, [auto] is not enough to make any progress. Since even a single
* candidate step may lead to an infinite space of possible proof trees,
* [auto] is parameterized on the maximum depth of trees to consider. The
* default depth is 5, and it turns out that we need depth 6 to prove the
* goal. *)
auto 6.
(* Sometimes it is useful to see a description of the proof tree that [auto]
* finds, with the [info_auto] variant. *)
Restart.
info_auto 6.
Qed.
(* The two key components of logic programming are _backtracking_ and
* _unification_. To see these techniques in action, consider this further
* silly example. Here our candidate proof steps will be reflexivity and
* quantifier instantiation. *)
Example seven_minus_three : exists x, x + 3 = 7.
Proof.
(* For explanatory purposes, let us simulate a user with minimal understanding
* of arithmetic. We start by choosing an instantiation for the quantifier.
* It is relevant that [ex_intro] is the proof rule for existential-quantifier
* instantiation. *)
apply ex_intro with 0.
Fail reflexivity.
(* This seems to be a dead end. Let us _backtrack_ to the point where we ran
* [apply] and make a better alternative choice. *)
Restart.
apply ex_intro with 4.
reflexivity.
Qed.
(* The above was a fairly tame example of backtracking. In general, any node in
* an under-construction proof tree may be the destination of backtracking an
* arbitrarily large number of times, as different candidate proof steps are
* found not to lead to full proof trees, within the depth bound passed to [auto].
*
* Next we demonstrate unification, which will be easier when we switch to the
* relational formulation of addition. *)
Example seven_minus_three' : exists x, plusR x 3 7.
Proof.
(* We could attempt to guess the quantifier instantiation manually as before,
* but here there is no need. Instead of [apply], we use [eapply], which
* proceeds with placeholder _unification variables_ standing in for those
* parameters we wish to postpone guessing. *)
eapply ex_intro.
(* Now we can finish the proof with the right applications of [plusR]'s
* constructors. Note that new unification variables are being generated to
* stand for new unknowns. *)
apply PlusS.
apply PlusS. apply PlusS. apply PlusS.
apply PlusO.
(* The [auto] tactic will not perform these sorts of steps that introduce
* unification variables, but the [eauto] tactic will. It is helpful to work
* with two separate tactics, because proof search in the [eauto] style can
* uncover many more potential proof trees and hence take much longer to
* run. *)
Restart.
info_eauto 6.
Qed.
(* This proof gives us our first example where logic programming simplifies
* proof search compared to functional programming. In general, functional
* programs are only meant to be run in a single direction; a function has
* disjoint sets of inputs and outputs. In the last example, we effectively ran
* a logic program backwards, deducing an input that gives rise to a certain
* output. The same works for deducing an unknown value of the other input. *)
Example seven_minus_four' : exists x, plusR 4 x 7.
Proof.
eauto 6.
Qed.
(* By proving the right auxiliary facts, we can reason about specific functional
* programs in the same way as we did above for a logic program. Let us prove
* that the constructors of [plusR] have natural interpretations as lemmas about
* [plus]. We can find the first such lemma already proved in the standard
* library, using the [Search] command to find a library function proving
* an equality whose lefthand or righthand side matches a pattern with
* wildcards. *)
Search (O + _).
(* The command [Hint Immediate] asks [auto] and [eauto] to consider this lemma
* as a candidate step for any leaf of a proof tree, meaning that all premises
* of the rule need to match hypotheses. *)
Local Hint Immediate plus_O_n : core.
(* The counterpart to [PlusS] we will prove ourselves. *)
Lemma plusS : forall n m r,
n + m = r
-> S n + m = S r.
Proof.
linear_arithmetic.
Qed.
(* The command [Hint Resolve] adds a new candidate proof step, to be attempted
* at any level of a proof tree, not just at leaves. *)
Local Hint Resolve plusS : core.
(* Now that we have registered the proper hints, we can replicate our previous
* examples with the normal, functional addition [plus]. *)
Example seven_minus_three'' : exists x, x + 3 = 7.
Proof.
eauto 6.
Qed.
Example seven_minus_four : exists x, 4 + x = 7.
Proof.
eauto 6.
Qed.
(* This new hint database is far from a complete decision procedure, as we see in
* a further example that [eauto] does not finish. *)
Example seven_minus_four_zero : exists x, 4 + x + 0 = 7.
Proof.
eauto 6.
Abort.
(* A further lemma will be helpful. *)
Lemma plusO : forall n m,
n = m
-> n + 0 = m.
Proof.
linear_arithmetic.
Qed.
Local Hint Resolve plusO : core.
(* Note that, if we consider the inputs to [plus] as the inputs of a
* corresponding logic program, the new rule [plusO] introduces an ambiguity.
* For instance, a sum [0 + 0] would match both of [plus_O_n] and [plusO],
* depending on which operand we focus on. This ambiguity may increase the
* number of potential search trees, slowing proof search, but semantically it
* presents no problems, and in fact it leads to an automated proof of the
* present example. *)
Example seven_minus_four_zero : exists x, 4 + x + 0 = 7.
Proof.
eauto 7.
Qed.
(* Just how much damage can be done by adding hints that grow the space of
* possible proof trees? A classic gotcha comes from unrestricted use of
* transitivity, as embodied in this library theorem about equality: *)
Check eq_trans.
(* Hints are scoped over sections, so let us enter a section to contain the
* effects of an unfortunate hint choice. *)
Section slow.
Hint Resolve eq_trans : core.
(* The following fact is false, but that does not stop [eauto] from taking a
* very long time to search for proofs of it. We use the handy [Time] command
* to measure how long a proof step takes to run. None of the following steps
* make any progress. *)
Example zero_minus_one : exists x, 1 + x = 0.
Time eauto 1.
Time eauto 2.
Time eauto 3.
Time eauto 4.
Time eauto 5.
(* We see worrying exponential growth in running time, and the [debug]
* tactical helps us see where [eauto] is wasting its time, outputting a
* trace of every proof step that is attempted. The rule [eq_trans] applies
* at every node of a proof tree, and [eauto] tries all such positions. *)
debug eauto 3.
Abort.
End slow.
(* Sometimes, though, transitivity is just what is needed to get a proof to go
* through automatically with [eauto]. For those cases, we can use named
* _hint databases_ to segregate hints into different groups that may be called
* on as needed. Here we put [eq_trans] into the database [slow]. *)
Local Hint Resolve eq_trans : slow.
Example from_one_to_zero : exists x, 1 + x = 0.
Proof.
Time eauto.
(* This [eauto] fails to prove the goal, but at least it takes substantially
* less than the 2 seconds required above! *)
Abort.
(* One simple example from before runs in the same amount of time, avoiding
* pollution by transitivity. *)
Example seven_minus_three_again : exists x, x + 3 = 7.
Proof.
Time eauto 6.
Qed.
(* When we _do_ need transitivity, we ask for it explicitly. *)
Example needs_trans : forall x y, 1 + x = y
-> y = 2
-> exists z, z + x = 3.
Proof.
info_eauto with slow.
Qed.
(* The [info] trace shows that [eq_trans] was used in just the position where it
* is needed to complete the proof. We also see that [auto] and [eauto] always
* perform [intro] steps without counting them toward the bound on proof-tree
* depth. *)
(** * Searching for Underconstrained Values *)
(* Recall the definition of the list length function. *)
Print Datatypes.length.
(* This function is easy to reason about in the forward direction, computing
* output from input. *)
Example length_1_2 : length (1 :: 2 :: nil) = 2.
Proof.
auto.
Qed.
Print length_1_2.
(* As in the last section, we will prove some lemmas to recast [length] in
* logic-programming style, to help us compute inputs from outputs. *)
Theorem length_O : forall A, length (nil (A := A)) = O.
Proof.
simplify; equality.
Qed.
Theorem length_S : forall A (h : A) t n,
length t = n
-> length (h :: t) = S n.
Proof.
simplify; equality.
Qed.
Local Hint Resolve length_O length_S : core.
(* Let us apply these hints to prove that a [list nat] of length 2 exists.
* (Here we register [length_O] with [Hint Resolve] instead of [Hint Immediate]
* merely as a convenience to use the same command as for [length_S]; [Resolve]
* and [Immediate] have the same meaning for a premise-free hint.) *)
Example length_is_2 : exists ls : list nat, length ls = 2.
Proof.
eauto.
(* Coq leaves for us two subgoals to prove... [nat]?! We are being asked to
* show that natural numbers exists. Why? Some unification variables of that
* type were left undetermined, by the end of the proof. Specifically, these
* variables stand for the 2 elements of the list we find. Of course it makes
* sense that the list length follows without knowing the data values. In Coq
* 8.6 and up, the [Unshelve] command brings these goals to the forefront,
* where we can solve each one with [exact O], but usually it is better to
* avoid getting to such a point.
*
* To debug such situations, it can be helpful to print the current internal
* representation of the proof, so we can see where the unification variables
* show up. *)
Show Proof.
Abort.
(* Paradoxically, we can make the proof-search process easier by constraining
* the list further, so that proof search naturally locates appropriate data
* elements by unification. The library predicate [Forall] will be helpful. *)
Print Forall.
Example length_is_2 : exists ls : list nat, length ls = 2
/\ Forall (fun n => n >= 1) ls.
Proof.
eauto 9.
Qed.
(* We can see which list [eauto] found by printing the proof term. *)
Print length_is_2.
(* Let us try one more, fancier example. First, we use a standard higher-order
* function to define a function for summing all data elements of a list. *)
Definition sum := fold_right plus O.
(* Another basic lemma will be helpful to guide proof search. *)
Lemma plusO' : forall n m,
n = m
-> 0 + n = m.
Proof.
linear_arithmetic.
Qed.
Local Hint Resolve plusO' : core.
(* Finally, we meet [Hint Extern], the command to register a custom hint. That
* is, we provide a pattern to match against goals during proof search.
* Whenever the pattern matches, a tactic (given to the right of an arrow [=>])
* is attempted. Below, the number [1] gives a priority for this step. Lower
* priorities are tried before higher priorities, which can have a significant
* effect on proof-search time, i.e. when we manage to give lower priorities to
* the cheaper rules. *)
Local Hint Extern 1 (sum _ = _) => simplify : core.
(* Now we can find a length-2 list whose sum is 0. *)
Example length_and_sum : exists ls : list nat, length ls = 2
/\ sum ls = O.
Proof.
eauto 7.
Qed.
Print length_and_sum.
(* Printing the proof term shows the unsurprising list that is found. Here is
* an example where it is less obvious which list will be used. Can you guess
* which list [eauto] will choose? *)
Example length_and_sum' : exists ls : list nat, length ls = 5
/\ sum ls = 42.
Proof.
eauto 15.
Qed.
Print length_and_sum'.
(* We will give away part of the answer and say that the above list is less
* interesting than we would like, because it contains too many zeroes. A
* further constraint forces a different solution for a smaller instance of the
* problem. *)
Example length_and_sum'' : exists ls : list nat, length ls = 2
/\ sum ls = 3
/\ Forall (fun n => n <> 0) ls.
Proof.
eauto 11.
Qed.
Print length_and_sum''.
(* We could continue through exercises of this kind, but even more interesting
* than finding lists automatically is finding _programs_ automatically. *)
(** * Synthesizing Programs *)
(* Here is a simple syntax type for arithmetic expressions, similar to those we
* have used several times before. In this case, we allow expressions to
* mention exactly one distinguished variable. *)
Inductive exp : Set :=
| Const (n : nat)
| Var
| Plus (e1 e2 : exp).
(* An inductive relation specifies the semantics of an expression, relating a
* variable value and an expression to the expression value. *)
Inductive eval (var : nat) : exp -> nat -> Prop :=
| EvalConst : forall n, eval var (Const n) n
| EvalVar : eval var Var var
| EvalPlus : forall e1 e2 n1 n2, eval var e1 n1
-> eval var e2 n2
-> eval var (Plus e1 e2) (n1 + n2).
Local Hint Constructors eval : core.
(* We can use [auto] to execute the semantics for specific expressions. *)
Example eval1 : forall var, eval var (Plus Var (Plus (Const 8) Var)) (var + (8 + var)).
Proof.
auto.
Qed.
(* Unfortunately, just the constructors of [eval] are not enough to prove
* theorems like the following, which depends on an arithmetic identity. *)
Example eval1' : forall var, eval var (Plus Var (Plus (Const 8) Var)) (2 * var + 8).
Proof.
eauto.
Abort.
(* To help prove [eval1'], we prove an alternative version of [EvalPlus] that
* inserts an extra equality premise. This sort of staging is helpful to get
* around limitations of [eauto]'s unification: [EvalPlus] as a direct hint will
* only match goals whose results are already expressed as additions, rather
* than as constants. With the alternative version below, to prove the first
* two premises, [eauto] is given free reign in deciding the values of [n1] and
* [n2], while the third premise can then be proved by [reflexivity], no matter
* how each of its sides is decomposed as a tree of additions. *)
Theorem EvalPlus' : forall var e1 e2 n1 n2 n, eval var e1 n1
-> eval var e2 n2
-> n1 + n2 = n
-> eval var (Plus e1 e2) n.
Proof.
simplify; subst; auto.
Qed.
Local Hint Resolve EvalPlus' : core.
(* Further, we instruct [eauto] to apply [ring], via [Hint Extern]. We should
* try this step for any equality goal. *)
Section use_ring.
Hint Extern 1 (_ = _) => ring : core.
(* Now we can return to [eval1'] and prove it automatically. *)
Example eval1' : forall var, eval var (Plus Var (Plus (Const 8) Var)) (2 * var + 8).
Proof.
eauto.
Qed.
(* Now we are ready to take advantage of logic programming's flexibility by
* searching for a program (arithmetic expression) that always evaluates to a
* particular symbolic value. *)
Example synthesize1 : exists e, forall var, eval var e (var + 7).
Proof.
eauto.
Qed.
Print synthesize1.
(* Here are two more examples showing off our program-synthesis abilities. *)
Example synthesize2 : exists e, forall var, eval var e (2 * var + 8).
Proof.
eauto.
Qed.
Print synthesize2.
Example synthesize3 : exists e, forall var, eval var e (3 * var + 42).
Proof.
eauto.
Qed.
Print synthesize3.
End use_ring.
(* These examples show linear expressions over the variable [var]. Any such
* expression is equivalent to [k * var + n] for some [k] and [n]. It is
* probably not so surprising that we can prove that any expression's semantics
* is equivalent to some such linear expression, but it is tedious to prove such
* a fact manually. To finish this section, we will use [eauto] to complete the
* proof, finding [k] and [n] values automatically.
*
* We prove a series of lemmas and add them as hints. We have alternate [eval]
* constructor lemmas and some facts about arithmetic. *)
Theorem EvalConst' : forall var n m, n = m
-> eval var (Const n) m.
Proof.
simplify; subst; auto.
Qed.
Theorem EvalVar' : forall var n,
var = n
-> eval var Var n.
Proof.
simplify; subst; auto.
Qed.
Local Hint Resolve EvalConst' EvalVar' : core.
(* Next, we prove a few hints that feel a bit like cheating, as they telegraph
* the procedure for choosing values of [k] and [n]. Nonetheless, with these
* lemmas in place, we achieve an automated proof without explicitly
* orchestrating the lemmas' composition. *)
Section cheap_hints.
Theorem zero_times : forall n m r,
r = m
-> r = 0 * n + m.
Proof.
linear_arithmetic.
Qed.
Theorem plus_0 : forall n r,
r = n
-> r = n + 0.
Proof.
linear_arithmetic.
Qed.
Theorem times_1 : forall n, n = 1 * n.
Proof.
linear_arithmetic.
Qed.
Theorem combine : forall x k1 k2 n1 n2,
(k1 * x + n1) + (k2 * x + n2) = (k1 + k2) * x + (n1 + n2).
Proof.
simplify; ring.
Qed.
Hint Resolve zero_times plus_0 times_1 combine : core.
Theorem linear : forall e, exists k n,
forall var, eval var e (k * var + n).
Proof.
induct e; first_order; eauto.
Qed.
End cheap_hints.
(* Let's try that again, without using those hints that give away the answer.
* We should be able to coerce Coq into doing more of the thinking for us. *)
(* First, we will want to be able to use built-in tactic [ring_simplify] on
* goals that contain unification variables, which will be the case at
* intermediate points in our proof search. We also want a bit more
* standardization on ordering of variables within multiplications, to increase
* the odds that different calculations can unify with each other. Here is a
* tactic that realizes those wishes. *)
Ltac robust_ring_simplify :=
(* First, introduce regular names for all unification variables, because
* [ring_simplify] freaks out when it meets a unification variable. *)
repeat match goal with
| [ |- context[?v] ] => is_evar v; remember v
end;
(* Now call the standard tactic. *)
ring_simplify;
(* Replace uses of the new variables with the original unification
* variables. *)
subst;
(* Find and correct cases where commutativity doesn't agree across subterms of
* the goal. *)
repeat match goal with
| [ |- context[?X * ?Y] ] =>
match goal with
| [ |- context[?Z * X] ] => rewrite (mult_comm Z X)
end
end.
(* This tactic is pretty expensive, but let's try it eventually whenever the
* goal is an equality. *)
Local Hint Extern 5 (_ = _) => robust_ring_simplify : core.
(* The only other missing ingredient is priming Coq with some good ideas for
* instantiating existential quantifiers. These will all be tried in some
* order, in a particular proof search. *)
Local Hint Extern 1 (exists n : nat, _) => exists 0 : core.
Local Hint Extern 1 (exists n : nat, _) => exists 1 : core.
Local Hint Extern 1 (exists n : nat, _) => eexists (_ + _) : core.
(* Note how this last hint uses [eexists] to provide an instantiation with
* wildcards inside it. Each underscore is replaced with a fresh unification
* variable. *)
(* Now Coq figures out the recipe for us, and quite quickly, at that! *)
Theorem linear_snazzy : forall e, exists k n,
forall var, eval var e (k * var + n).
Proof.
induct e; first_order; eauto.
Qed.
(* Here's a quick tease using a feature that we'll explore fully in a later
* class. Let's use a mysterious construct [sigT] instead of [exists]. *)
Local Hint Extern 1 (sigT (fun n : nat => _)) => exists 0 : core.
Local Hint Extern 1 (sigT (fun n : nat => _)) => exists 1 : core.
Local Hint Extern 1 (sigT (fun n : nat => _)) => eexists (_ + _) : core.
Theorem linear_computable : forall e, sigT (fun k => sigT (fun n =>
forall var, eval var e (k * var + n))).
Proof.
induct e; first_order; eauto.
Defined.
(* Essentially the same proof search has completed. This time, though, we ended
* the proof with [Defined], which saves it as _transparent_, so details of the
* "proof" can be consulted from the outside. Actually, this "proof" is more
* like a recursive program that finds [k] and [n], given [e]! Let's add a
* wrapper to make that idea more concrete. *)
Definition params (e : exp) : nat * nat :=
let '(existT _ k (existT _ n _)) := linear_computable e in
(k, n).
(* Now we can actually _run our proof_ to get normalized versions of particular
* expressions. *)
Compute params (Plus (Const 7) (Plus Var (Plus (Const 8) Var))).
(* With Coq doing so much of the proof-search work for us, we might get
* complacent and consider that any successful [eauto] invocation is as good as
* any other. However, because introduced unification variables may wind up
* spread across multiple subgoals, running [eauto] can have a surprising kind
* of _side effect_ across goals! When a proof isn't solved completely by one
* [eauto] call, the cross-subgoal side effects can break proofs that used to
* work, when we introduce new premises or hints. Here's an illustrative
* example. *)
Section side_effect_sideshow.
Variable A : Set.
Variables P Q : A -> Prop.
Variable x : A.
Hypothesis Px : P x.
Hypothesis Qx : Q x.
Theorem double_threat : exists y, P y /\ Q y.
Proof.
eexists; propositional.
eauto.
eauto.
Qed.
(* That was easy enough. [eauto] could have solved the whole thing, but humor
* me by considering this slightly less-automated proof. Watch what happens
* when we add a new premise. *)
Variable z : A.
Hypothesis Pz : P z.
Theorem double_threat' : exists y, P y /\ Q y.
Proof.
eexists; propositional.
eauto.
eauto.
(* Oh no! The second subgoal isn't true anymore, because the first [eauto]
* now matched [Pz] instead of [Px]. *)
Restart.
eauto.
(* [eauto] can still find the whole proof, though. *)
Qed.
End side_effect_sideshow.
(* Again, the moral of the story is: while proof search in Coq often feels
* purely functional, unification variables allow imperative side effects to
* reach across subgoals. That's a tremendously useful feature for effective
* proof automation, but it can also sneak up on you at times. *)
(** * More on [auto] Hints *)
(* Let us stop at this point and take stock of the possibilities for [auto] and
* [eauto] hints. Hints are contained within _hint databases_, which we have
* seen extended in many examples so far. When no hint database is specified, a
* default database is used. Hints in the default database are always used by
* [auto] or [eauto]. The chance to extend hint databases imperatively is
* important, because, in Ltac programming, we cannot create "global variables"
* whose values can be extended seamlessly by different modules in different
* source files. We have seen the advantages of hints so far, where a proof
* script using [auto] or [eauto] can automatically adapt to presence of new
* hints.
*
* The basic hints for [auto] and [eauto] are:
* - [Hint Immediate lemma], asking to try solving a goal immediately by
* applying a lemma and discharging any hypotheses with a single proof step
* each
* - [Hint Resolve lemma], which does the same but may add new premises that are
* themselves to be subjects of nested proof search
* - [Hint Constructors pred], which acts like [Resolve] applied to every
* constructor of an inductive predicate
* - [Hint Unfold ident], which tries unfolding [ident] when it appears at the
* head of a proof goal
* Each of these [Hint] commands may be used with a suffix, as in
* [Hint Resolve lemma : my_db], to add the hint only to the specified database,
* so that it would only be used by, for instance, [auto with my_db]. An
* additional argument to [auto] specifies the maximum depth of proof trees to
* search in depth-first order, as in [auto 8] or [auto 8 with my_db]. The
* default depth is 5.
*
* All of these [Hint] commands can be expressed with a more primitive hint
* kind, [Extern]. A few more examples of [Hint Extern] should illustrate more
* of the possibilities. *)
Theorem bool_neq : true <> false.
Proof.
auto.
(* No luck so far. *)
Abort.
(* It is hard to come up with a [bool]-specific hint that is not just a
* restatement of the theorem we mean to prove. Luckily, a simpler form
* suffices, by appealing to the [equality] tactic. *)
Local Hint Extern 1 (_ <> _) => equality : core.
Theorem bool_neq : true <> false.
Proof.
auto.
Qed.
(* A [Hint Extern] may be implemented with the full Ltac language. This example
* shows a case where a hint uses a [match]. *)
Section forall_and.
Variable A : Set.
Variables P Q : A -> Prop.
Hypothesis both : forall x, P x /\ Q x.
Theorem forall_and : forall z, P z.
Proof.
auto.
(* The [auto] invocation makes no progress beyond what [intros] would have
* accomplished. An [auto] call will not apply the hypothesis [both] to
* prove the goal, because the conclusion of [both] does not unify with the
* conclusion of the goal. However, we can teach [auto] to handle this
* kind of goal. *)
Hint Extern 1 (P ?X) =>
match goal with
| [ H : forall x, P x /\ _ |- _ ] => apply (proj1 (H X))
end : core.
auto.
Qed.
(* We see that an [Extern] pattern may bind unification variables that we use
* in the associated tactic. The function [proj1] is from the standard
* library, for extracting a proof of [U] from a proof of [U /\ V]. *)
End forall_and.
(* After our success on this example, we might get more ambitious and seek to
* generalize the hint to all possible predicates [P]. *)
Fail Local Hint Extern 1 (?P ?X) =>
match goal with
| [ H : forall x, P x /\ _ |- _ ] => apply (proj1 (H X))
end : core.
(* Coq's [auto] hint databases work as tables mapping _head symbols_ to lists of
* tactics to try. Because of this, the constant head of an [Extern] pattern
* must be determinable statically. In our first [Extern] hint, the head symbol
* was [not], since [x <> y] desugars to [not (x = y)]; and, in the second
* example, the head symbol was [P].
*
* Fortunately, a more basic form of [Hint Extern] also applies. We may simply
* leave out the pattern to the left of the [=>], incorporating the
* corresponding logic into the Ltac script. *)
Local Hint Extern 1 =>
match goal with
| [ H : forall x, ?P x /\ _ |- ?P ?X ] => apply (proj1 (H X))
end : core.
(* Be forewarned that a [Hint Extern] of this kind will be applied at _every_
* node of a proof tree, so an expensive Ltac script may slow proof search
* significantly. *)
(** * Rewrite Hints *)
(* Another dimension of extensibility with hints is rewriting with quantified
* equalities. We have used the associated command [Hint Rewrite] in several
* examples so far. The [simplify] tactic uses these hints by calling the
* built-in tactic [autorewrite]. Our rewrite hints have taken the form
* [Hint Rewrite lemma], which by default adds them to the default hint database
* [core]; but alternate hint databases may also be specified just as with,
* e.g., [Hint Resolve].
*
* The next example shows a direct use of [autorewrite]. Note that, while
* [Hint Rewrite] uses a default database, [autorewrite] requires that a
* database be named. *)
Section autorewrite.
Variable A : Set.
Variable f : A -> A.
Hypothesis f_f : forall x, f (f x) = f x.
Hint Rewrite f_f.
Lemma f_f_f : forall x, f (f (f x)) = f x.
Proof.
intros; autorewrite with core; reflexivity.
Qed.
(* There are a few ways in which [autorewrite] can lead to trouble when
* insufficient care is taken in choosing hints. First, the set of hints may
* define a nonterminating rewrite system, in which case invocations to
* [autorewrite] may not terminate. Second, we may add hints that "lead
* [autorewrite] down the wrong path." For instance: *)
Section garden_path.
Variable g : A -> A.
Hypothesis f_g : forall x, f x = g x.
Hint Rewrite f_g.
Lemma f_f_f' : forall x, f (f (f x)) = f x.
Proof.
intros; autorewrite with core.
Abort.
(* Our new hint was used to rewrite the goal into a form where the old hint
* could no longer be applied. This "non-monotonicity" of rewrite hints
* contrasts with the situation for [auto], where new hints may slow down
* proof search but can never "break" old proofs. The key difference is
* that [auto] either solves a goal or makes no changes to it, while
* [autorewrite] may change goals without solving them. The situation for
* [eauto] is slightly more complicated, as changes to hint databases may
* change the proof found for a particular goal, and that proof may
* influence the settings of unification variables that appear elsewhere in
* the proof state. *)
Reset garden_path.
(* The [autorewrite] tactic also works with quantified equalities that include
* additional premises, but we must be careful to avoid similar incorrect
* rewritings. *)
Section garden_path.
Variable P : A -> Prop.
Variable g : A -> A.
Hypothesis f_g : forall x, P x -> f x = g x.
Hint Rewrite f_g.
Lemma f_f_f' : forall x, f (f (f x)) = f x.
Proof.
intros; autorewrite with core.
Abort.
(* The inappropriate rule fired the same three times as before, even though
* we know we will not be able to prove the premises. *)
Reset garden_path.
(* Our final, successful attempt uses an extra argument to [Hint Rewrite] that
* specifies a tactic to apply to generated premises. Such a hint is only
* used when the tactic succeeds for all premises, possibly leaving further
* subgoals for some premises. *)
Section garden_path.
Variable P : A -> Prop.
Variable g : A -> A.
Hypothesis f_g : forall x, P x -> f x = g x.
Hint Rewrite f_g using assumption.
Lemma f_f_f' : forall x, f (f (f x)) = f x.
Proof.
intros; autorewrite with core; reflexivity.
Qed.
(* We may still use [autorewrite] to apply [f_g] when the generated premise
* is among our assumptions. *)
Lemma f_f_f_g : forall x, P x -> f (f x) = g x.
Proof.
intros; autorewrite with core; reflexivity.
Qed.
End garden_path.
(* It can also be useful to apply the [autorewrite with db in *] form, which
* does rewriting in hypotheses, as well as in the conclusion. *)
Lemma in_star : forall x y, f (f (f (f x))) = f (f y)
-> f x = f (f (f y)).
Proof.
intros; autorewrite with core in *; assumption.
Qed.
End autorewrite.
(* Many proofs can be automated in pleasantly modular ways with deft
* combinations of [auto] and [autorewrite]. *)