-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathSwap.v
580 lines (505 loc) · 15.6 KB
/
Swap.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
(*
* DEVOID supports swapping and renaming constructors!
* Here are some examples.
*)
Require Import List.
Require Import String.
Require Import ZArith.
Require Import Vector.
Import ListNotations.
Require Import Ornamental.Ornaments.
Set DEVOID search prove equivalence.
Set DEVOID lift type.
(* --- Swap the only constructor --- *)
(*
* Here we simply flip the constructors of list and then
* lift the entire list module:
*)
Inductive list' (T : Type) : Type :=
| cons' : T -> list' T -> list' T
| nil' : list' T.
(* Preprocess for lifting: *)
Preprocess Module List as List_pre { opaque (* ignore these nested modules: *)
RelationClasses
Nat Coq.Init.Nat
Coq.Init.Logic Coq.Init.Peano
Coq.Init.Datatypes.list_ind Coq.Init.Datatypes.list_rect Coq.Init.Datatypes.list_rec
Coq.Init.Datatypes.nat_ind Coq.Init.Datatypes.nat_rect Coq.Init.Datatypes.nat_rec
eq_ind eq_ind_r eq_rec eq_rec_r eq_rect eq_rect_r
}.
(* Lift the whole list module: *)
Repair Module list list' in List_pre as List' {
opaque (* ignore these, just for speed *)
RelationClasses.Equivalence_Reflexive
RelationClasses.reflexivity
Nat.add
Nat.sub
Nat.lt_eq_cases
Nat.compare_refl
Nat.lt_irrefl
Nat.le_refl
Nat.bi_induction
Nat.central_induction;
hint (* some tacic hints for better scripts *)
"auto"
}.
(* That should generate tactics for a whole bunch of these, but just to check
the paper example (has explicit A whereas paper has implicit A,
and passes in a tactic hint to try auto instead of reflexivity): *)
Repair list list' in List_pre.rev_app_distr as rev_app_distr' { hint "auto" }.
(* The tactics for section and retraction are in the output of Repair Module above
(at the top). *)
(* Example from the overview that shows append is OK *)
Definition swap := List'.list_to_list'.
Definition app' := List'.Coq_Init_Datatypes_app.
Definition app := List_pre.Coq_Init_Datatypes_app.
Lemma app_ok:
forall T (l1 l2 : list T),
swap T (app T l1 l2) =
app' T (swap T l1) (swap T l2).
Proof.
intros. induction l1.
- auto.
- simpl. rewrite IHl1. reflexivity.
Defined.
(*
* We get this for free, whereas the original tactic script doesn't work here,
* so even for a single proof we save development time.
* We have all of the updated functions and proofs here with no additional effort.
* To get tactics that match the original style, though, we might still need
* to tweak the original script.
*
* So we get a significant time savings over manually fixing the entire standard
* library (no time for every single function and proof, while a number of the
* functions and proofs break), but for now at some cost to readability.
* See this video for a comparison: https://www.youtube.com/watch?v=JINV13wNgIQ
*
* If we want readable proofs that match the original style, we may need to tweak
* the output tactic scripts, which may add back some effort (though for the proof
* above it is simple).
*)
(* The tactics for section and retraction are in the output of Repair Module. *)
(* A small test in the opposite direction that doesn't rely on caching: *)
Lemma my_lemma:
forall (T : Type) (l : list' T),
List'.Coq_Init_Datatypes_app T l (nil' T) = List'.Coq_Init_Datatypes_app T (nil' T) l.
Proof.
intros T l. induction l.
- simpl. simpl in IHl. rewrite IHl. reflexivity.
- reflexivity.
Defined.
Repair list' list in my_lemma as my_lemma_lifted.
(* Trying the above suggested tactics:*)
Lemma my_lemma_lifted_test:
forall (T : Type) (l : list T),
List_pre.Coq_Init_Datatypes_app T l [] =
List_pre.Coq_Init_Datatypes_app T [] l.
Proof.
intros T l. induction l as [ |t l0 IHl].
- reflexivity.
- simpl. rewrite IHl. reflexivity.
Defined.
(* --- Composing with algebraic ornaments --- *)
(*
* We can compose this with an algebraic ornament:
*)
Inductive vector' (T : Type) : nat -> Type :=
| consV' : T -> forall (n : nat), vector' T n -> vector' T (S n)
| nilV' : vector' T 0.
(* Suggested tactics sometimes aren't useful for dependent types yet, so we use Lift: *)
Lift list' vector' in List'.Coq_Init_Datatypes_app as appV'.
Lift list' vector' in my_lemma as my_lemmaV'.
Lift vector' Vector.t in appV' as appV.
Lift vector' Vector.t in my_lemmaV' as my_lemmaV.
(*
* Note that these commute:
*)
Lift list Vector.t in List_pre.Coq_Init_Datatypes_app as appV2.
Lift list Vector.t in my_lemma_lifted as my_lemmaV2.
Lemma test_app_commutes:
appV = appV2.
Proof.
reflexivity.
Qed.
Lemma test_my_lemma_commutes:
my_lemmaV = my_lemmaV2.
Proof.
reflexivity.
Qed.
(* --- An ambiguous swap --- *)
(*
* This type comes from the REPLICA benchmarks.
* This is a real user change (though there were other
* changes at the same time). We don't include the user's
* admitted theorems.
*)
Definition Identifier := string.
Definition id_eq_dec := string_dec.
Inductive Term : Set :=
| Var : Identifier -> Term
| Int : Z -> Term
| Eq : Term -> Term -> Term
| Plus : Term -> Term -> Term
| Times : Term -> Term -> Term
| Minus : Term -> Term -> Term
| Choose : Identifier -> Term -> Term.
Module User5Session19.
Definition extendEnv {Value} (env : Identifier -> Value)
(var : Identifier) (newValue : Value) : Identifier -> Value :=
fun id => if id_eq_dec id var then newValue else env id.
Record EpsilonLogic :=
mkLogic {Value : Type;
value_eq_dec : forall v1 v2 : Value, {v1 = v2} + {v1 <> v2};
vTrue : Value;
vFalse : Value;
trueAndFalseDistinct : vTrue <> vFalse;
eval : (Identifier -> Value) -> Term -> Value;
evalVar : forall env id, eval env (Var id) = env id;
evalIntConst :
forall env1 env2 i, eval env1 (Int i) = eval env2 (Int i);
evalIntInj :
forall env i j, i <> j -> eval env (Int i) <> eval env (Int j);
evalEqTrue :
forall env a b,
eval env a = eval env b <-> eval env (Eq a b) = vTrue;
evalEqFalse :
forall env a b,
eval env a <> eval env b <-> eval env (Eq a b) = vFalse;
evalPlus :
forall env iE jE i j,
eval env iE = eval env (Int i) ->
eval env jE = eval env (Int j) ->
eval env (Plus iE jE) = eval env (Int (i + j));
evalMinus :
forall env iE jE i j,
eval env iE = eval env (Int i) ->
eval env jE = eval env (Int j) ->
eval env (Minus iE jE) = eval env (Int (i - j));
evalTimes :
forall env iE jE i j,
eval env iE = eval env (Int i) ->
eval env jE = eval env (Int j) ->
eval env (Times iE jE) = eval env (Int (i * j));
evalChoose :
forall env x P,
(exists value, eval (extendEnv env x value) P = vTrue) ->
eval (extendEnv env x (eval env (Choose x P))) P = vTrue;
evalChooseDet :
forall env x P Q,
eval env P = vTrue <-> eval env Q = vTrue ->
eval env (Choose x P) = eval env (Choose x Q)}.
Definition isTheorem (L : EpsilonLogic) (t : Term) :=
forall env, L.(eval) env t = L.(vTrue).
Fixpoint identity (t : Term) : Term :=
match t with
| Var x => Var x
| Int i => Int i
| Eq a b => Eq (identity a) (identity b)
| Plus a b => Plus (identity a) (identity b)
| Times a b => Times (identity a) (identity b)
| Minus a b => Minus (identity a) (identity b)
| Choose x P => Choose x (identity P)
end.
Theorem eval_eq_true_or_false :
forall (L : EpsilonLogic) env (t1 t2 : Term),
L.(eval) env (Eq t1 t2) = L.(vTrue) \/ L.(eval) env (Eq t1 t2) = L.(vFalse).
Proof.
(intros).
(destruct (L.(value_eq_dec) (L.(eval) env t1) (L.(eval) env t2)) eqn:E).
-
left.
(apply L.(evalEqTrue)).
assumption.
-
right.
(apply L.(evalEqFalse)).
assumption.
Qed.
Fixpoint free_vars (t : Term) : list Identifier :=
match t with
| Var x => [x]
| Int _ => []
| Eq a b => free_vars a ++ free_vars b
| Plus a b => free_vars a ++ free_vars b
| Times a b => free_vars a ++ free_vars b
| Minus a b => free_vars a ++ free_vars b
| Choose x P =>
filter (fun y => if id_eq_dec x y then false else true) (free_vars P)
end.
End User5Session19.
Preprocess Module User5Session19 as User5Session19_pre.
Inductive Term' : Set :=
| Var' : Identifier -> Term'
| Eq' : Term' -> Term' -> Term'
| Int' : Z -> Term'
| Plus' : Term' -> Term' -> Term'
| Times' : Term' -> Term' -> Term'
| Minus' : Term' -> Term' -> Term'
| Choose' : Identifier -> Term' -> Term'.
(*
* Note the swap here is ambiguous because we don't know
* which constructor we swapped Int with. It could have been Eq,
* but also Plus, Times, or Minus. So we should drop into
* proof mode and ask the user when this happens.
*)
Fail Find ornament Term Term'. (* for now, we tell the user to pick one via an error *)
Find ornament Term Term' { mapping 0 }. (* we pick one this way *)
(*
* We can now lift everything (failures are just silly attempts to lift record
* projections twice; safe to ignore them):
*)
Lift Module Term Term' in User5Session19_pre as User5Session19'.
(*
* Suggested tactics here would need some work (can generate them with Repair,
* they just aren't very useful yet).
*)
Lemma test_eval_eq_true_or_false:
forall (L : User5Session19'.EpsilonLogic)
env
(t1 t2 : Term'),
User5Session19'.eval L env (Eq' t1 t2) = User5Session19'.vTrue L \/
User5Session19'.eval L env (Eq' t1 t2) = User5Session19'.vFalse L.
Proof.
exact User5Session19'.eval_eq_true_or_false.
Qed.
(* --- A more ambiguous swap --- *)
(*
* We can continue down that line but this time swap two
* constructors with the same type.
*)
Inductive Term'' : Set :=
| Var'' : Identifier -> Term''
| Eq'' : Term'' -> Term'' -> Term''
| Int'' : Z -> Term''
| Minus'' : Term'' -> Term'' -> Term''
| Plus'' : Term'' -> Term'' -> Term''
| Times'' : Term'' -> Term'' -> Term''
| Choose'' : Identifier -> Term'' -> Term''.
Find ornament Term' Term'' { mapping 8 }.
(*
* We can now lift everything (failures are just silly attempts to lift record
* projections twice; safe to ignore them):
*)
Lift Module Term' Term'' in User5Session19' as User5Session19''.
Lemma test_eval_eq_true_or_false_2:
forall (L : User5Session19''.EpsilonLogic)
env
(t1 t2 : Term''),
User5Session19''.eval L env (Eq'' t1 t2) = User5Session19''.vTrue L \/
User5Session19''.eval L env (Eq'' t1 t2) = User5Session19''.vFalse L.
Proof.
exact User5Session19''.eval_eq_true_or_false.
Qed.
(* --- Note that we can do several swaps at once --- *)
(*
* This just skips an intermediate step and lifts from Term' directly to Term'',
* though we redefine it as Term''' to break caching.
*)
Inductive Term''' : Set :=
| Var''' : Identifier -> Term'''
| Eq''' : Term''' -> Term''' -> Term'''
| Int''' : Z -> Term'''
| Minus''' : Term''' -> Term''' -> Term'''
| Plus''' : Term''' -> Term''' -> Term'''
| Times''' : Term''' -> Term''' -> Term'''
| Choose''' : Identifier -> Term''' -> Term'''.
Find ornament Term Term''' { mapping 8 }.
Lift Module Term Term''' in User5Session19_pre as User5Session19'''.
Lemma test_eval_eq_true_or_false_3:
forall (L : User5Session19'''.EpsilonLogic)
env
(t1 t2 : Term'''),
User5Session19'''.eval L env (Eq''' t1 t2) = User5Session19'''.vTrue L \/
User5Session19'''.eval L env (Eq''' t1 t2) = User5Session19'''.vFalse L.
Proof.
exact User5Session19'''.eval_eq_true_or_false.
Qed.
(* --- Renaming --- *)
(*
* Note that renaming constructors is just the identity swap.
*)
Inductive Expr : Set :=
| Name : Identifier -> Expr
| Equal : Expr -> Expr -> Expr
| Number : Z -> Expr
| Subtract : Expr -> Expr -> Expr
| Add : Expr -> Expr -> Expr
| Multiply : Expr -> Expr -> Expr
| Choice : Identifier -> Expr -> Expr.
Find ornament Term''' Expr { mapping 0 }.
(*
* We can now lift everything (failures are just silly attempts to lift record
* projections twice; safe to ignore them):
*)
Lift Module Term''' Expr in User5Session19''' as CustomRenaming.
Lemma test_eval_equal_true_or_false:
forall (L : CustomRenaming.EpsilonLogic)
env
(e1 e2 : Expr),
CustomRenaming.eval L env (Equal e1 e2) = CustomRenaming.vTrue L \/
CustomRenaming.eval L env (Equal e1 e2) = CustomRenaming.vFalse L.
Proof.
exact CustomRenaming.eval_eq_true_or_false.
Qed.
(* --- Large and ambiguous --- *)
(*
* Here there are so many possible swaps that it makes no sense
* to show all of them. We show 50. The first one is renaming:
*)
Inductive Enum : Set :=
| e1 : Enum
| e2 : Enum
| e3 : Enum
| e4 : Enum
| e5 : Enum
| e6 : Enum
| e7 : Enum
| e8 : Enum
| e9 : Enum
| e10 : Enum
| e11 : Enum
| e12 : Enum
| e13 : Enum
| e14 : Enum
| e15 : Enum
| e16 : Enum
| e17 : Enum
| e18 : Enum
| e19 : Enum
| e20 : Enum
| e21 : Enum
| e22 : Enum
| e23 : Enum
| e24 : Enum
| e25 : Enum
| e26 : Enum
| e27 : Enum
| e28 : Enum
| e29 : Enum
| e30 : Enum.
Inductive Enum' : Set :=
| e1' : Enum'
| e2' : Enum'
| e3' : Enum'
| e4' : Enum'
| e5' : Enum'
| e6' : Enum'
| e7' : Enum'
| e8' : Enum'
| e9' : Enum'
| e10' : Enum'
| e11' : Enum'
| e12' : Enum'
| e13' : Enum'
| e14' : Enum'
| e15' : Enum'
| e16' : Enum'
| e17' : Enum'
| e18' : Enum'
| e19' : Enum'
| e20' : Enum'
| e21' : Enum'
| e22' : Enum'
| e23' : Enum'
| e24' : Enum'
| e25' : Enum'
| e26' : Enum'
| e27' : Enum'
| e28' : Enum'
| e29' : Enum'
| e30' : Enum'.
Find ornament Enum Enum' { mapping 0 }.
Definition is_e10 (e : Enum) :=
match e with
| e10 => True
| _ => False
end.
Preprocess is_e10 as is_e10_pre.
Lift Enum Enum' in is_e10_pre as is_e10'.
Lemma e10'_is_e10':
is_e10' e10'.
Proof.
reflexivity.
Defined.
(* --- Custom mapping --- *)
(*
* If the mapping we want doesn't show up in the top 50 candidates, we can
* supply our own using "Save ornament". We only need to provide "Save ornament"
* with one of two directions. It can find the other for us and prove
* the equivalence!
*)
Program Definition Enum_Enum' : Enum -> Enum'.
Proof.
intros e. induction e.
- apply e30'.
- apply e29'.
- apply e28'.
- apply e27'.
- apply e26'.
- apply e25'.
- apply e24'.
- apply e23'.
- apply e22'.
- apply e21'.
- apply e10'.
- apply e9'.
- apply e8'.
- apply e7'.
- apply e6'.
- apply e5'.
- apply e4'.
- apply e3'.
- apply e2'.
- apply e1'.
- apply e20'.
- apply e19'.
- apply e18'.
- apply e17'.
- apply e16'.
- apply e15'.
- apply e14'.
- apply e13'.
- apply e12'.
- apply e11'.
Defined.
Save ornament Enum Enum' { promote = Enum_Enum' }.
Definition is_e3 (e : Enum) :=
match e with
| e3 => True
| _ => False
end.
Preprocess is_e3 as is_e3_pre.
Lift Enum Enum' in is_e3_pre as is_e28'.
Lemma e28'_is_e28':
is_e28' e28'.
Proof.
reflexivity.
Defined.
Definition is_e3' (e : Enum') :=
match e with
| e3' => True
| _ => False
end.
Preprocess is_e3' as is_e3'_pre.
Lift Enum' Enum in is_e3'_pre as is_e18.
Lemma e18_is_e18:
is_e18 e18.
Proof.
reflexivity.
Defined.
(*
* The effort here is comparable since we supply the function, which is exactly
* as hard as writing the updated functions for each of these. Though it would
* likely save us development time to use PUMPKIN Pi if we were to look at not
* just functions, but also proofs, especially more complex proofs. Still, this
* scales to large ambiguous types without much overhead, just a single function.
* We could just as well have provided forget.
*
* Do note that if you change the equivalence when you run
* "Save ornament", this will not clear cached lifted terms,
* which may give you confusing results later if you lift using
* two different equivalences between the same types at different
* points in your code that depend on one another. If this is a
* problem for you, let us know and we can make it possible to
* clear the lifting cache.
*)