forked from plclub/metalib
-
Notifications
You must be signed in to change notification settings - Fork 0
/
STLC.v
1904 lines (1540 loc) · 58 KB
/
STLC.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
998
999
1000
(*************************************************************************)
(** The simply-typed lambda calculus in Coq. *)
(*************************************************************************)
(** An interactive tutorial on developing programming language
metatheory. This file uses the simply-typed lambda calculus
(STLC) to demonstrate the locally nameless representation of
lambda terms and cofinite quantification in judgments.
This tutorial concentrates on "how" to formalize STLC; for more
details about "why" we use this style of development see:
"Engineering Formal Metatheory", Aydemir, Chargu'eraud, Pierce,
Pollack, Weirich. POPL 2008.
Tutorial authors: Brian Aydemir and Stephanie Weirich, with help
from Aaron Bohannon, Nate Foster, Benjamin Pierce, Jeffrey
Vaughan, Dimitrios Vytiniotis, and Steve Zdancewic. Adapted from
code by Arthur Chargu'eraud.
Draft of 7/14/2009
*)
(*************************************************************************)
(** * Contents
- Syntax of STLC
- Substitution
- Free variables
- Open
- Local closure
- Properties about basic operations
- Cofinite quantification
- Tactic support
- Typing environments
- Typing relation
- Weakening
- Substitution
- Values and evaluation
- Preservation
- Progress
- Additional properties
- Renaming
- Decidability of typechecking
- Equivalence of exist fresh and cofinite
Solutions to exercises are in [STLCsol.v].
*)
(*************************************************************************)
(** First, we import a number of definitions from the Metatheory
library (see Metatheory.v). The following command makes those
definitions available in the rest of this file. This command will
only succeed if you have already run "make" in the tutorial
directory to compile the Metatheory library.
*)
Require Import Metatheory.
(*************************************************************************)
(** * Syntax of STLC *)
(*************************************************************************)
(** We use a locally nameless representation for the simply-typed
lambda calculus, where bound variables are represented as natural
numbers (de Bruijn indices) and free variables are represented as
[atom]s.
The type [atom], defined in the MetatheoryAtom library, represents
names. Equality on names is decidable ([eq_atom_dec]), and it is
possible to generate an atom fresh for any given finite set of
atoms ([atom_fresh]).
*)
Inductive typ : Set :=
| typ_base : typ
| typ_arrow : typ -> typ -> typ.
Inductive exp : Set :=
| bvar : nat -> exp
| fvar : atom -> exp
| abs : typ -> exp -> exp
| app : exp -> exp -> exp.
Coercion bvar : nat >-> exp.
Coercion fvar : atom >-> exp.
(** We declare the constructors for indices and variables to be
coercions. That way, if Coq sees a [nat] where it expects an
[exp], it will implicitly insert an application of [bvar]; and
similarly for [atom]s.
For example, we can encode the expression (\x:b. Y x) as below.
Because "Y" is free variable in this term, we need to assume an
atom for this name.
*)
Parameter Y : atom.
Definition demo_rep1 := abs typ_base (app Y 0).
(** Note that because of the coercions we may write [abs (app Y 0)]
instead of [abs (app (fvar Y) (bvar 0))].
Below is another example: the encoding of (\x:b. \y:b. (y x)).
*)
Definition demo_rep2 := abs typ_base (abs typ_base (app 0 1)).
(** Exercise: Uncomment and then complete the definitions of the following
lambda calculus terms using the locally nameless representation.
"two" : \s:b->b. \z:b. s (s z)
"COMB_K" : \x:b. \y:b. x
"COMB_S" : \x:b -> b -> b.\y:b -> b.\z:b. x z (y z)
*)
(** <<
Definition two :=
(* FILL IN HERE *)
Definition COMB_K :=
(* FILL IN HERE *)
Definition COMB_S :=
(* FILL IN HERE *)
>> *)
(** There are two important advantages of the locally nameless
representation:
- Alpha-equivalent terms have a unique representation.
We're always working up to alpha-equivalence.
- Operations such as free variable substitution and free
variable calculation have simple recursive definitions
(and therefore are simple to reason about).
Weighed against these advantages are two drawbacks:
- The [exp] datatype admits terms, such as [abs 3], where
indices are unbound.
A term is called "locally closed" when it contains
no unbound indices.
- We must define *both* bound variable & free variable
substitution and reason about how these operations
interact with each other.
*)
(*************************************************************************)
(** * Substitution *)
(*************************************************************************)
(** Substitution replaces a free variable with a term. The definition
below is simple for two reasons:
- Because bound variables are represented using indices, there
is no need to worry about variable capture.
- We assume that the term being substituted in is locally
closed. Thus, there is no need to shift indices when
passing under a binder.
*)
Fixpoint subst (z : atom) (u : exp) (e : exp)
{struct e} : exp :=
match e with
| bvar i => bvar i
| fvar x => if x == z then u else (fvar x)
| abs t e1 => abs t (subst z u e1)
| app e1 e2 => app (subst z u e1) (subst z u e2)
end.
(** The [Fixpoint] keyword defines a Coq function. As all functions
in Coq must be total. The annotation [{struct e}] indicates the
termination metric---all recursive calls in this definition are
made to arguments that are structurally smaller than [e].
Note also that subst uses the notation [x == z] for decidable atom
equality. This notation is defined in the Metatheory library.
We define a notation for free variable substitution that mimics
standard mathematical notation.
*)
Notation "[ z ~> u ] e" := (subst z u e) (at level 68).
(** To demonstrate how free variable substitution works, we need to
reason about atom equality.
*)
Parameter Z : atom.
Check (Y == Z).
(** The decidable atom equality function returns a sum. If the two
atoms are equal, the left branch of the sum is returned, carrying
a proof of the proposition that the atoms are equal. If they are
not equal, the right branch includes a proof of the disequality.
The demo below uses three new tactics:
- The tactic [simpl] reduces a Coq expression to its normal
form.
- The tactic [destruct (Y==Y)] considers the two possible
results of the equality test.
- The tactic [Case] marks cases in the proof script.
It takes any string as its argument, and puts that string in
the hypothesis list until the case is finished.
*)
Lemma demo_subst1:
[Y ~> Z] (abs typ_base (app 0 Y)) = (abs typ_base (app 0 Z)).
Proof.
simpl.
destruct (Y==Y).
Case "left".
auto.
Case "right".
destruct n. auto.
Qed.
(** Take-home Exercise: We can use almost the same proof script as
above to state how substitution works in the variable case. Try it
on your own. *)
Lemma subst_eq_var: forall (x : atom) u,
[x ~> u]x = u.
Proof.
(* OPTIONAL EXERCISE *) Admitted.
Lemma subst_neq_var : forall (x y : atom) u,
y <> x -> [x ~> u]y = y.
Proof.
(* OPTIONAL EXERCISE *) Admitted.
(*************************************************************************)
(** * Free variables *)
(*************************************************************************)
(** The function [fv], defined below, calculates the set of free
variables in an expression. Because we are using a locally
nameless representation, where bound variables are represented as
indices, any name we see is a free variable of a term. In
particular, this makes the [abs] case simple.
*)
Fixpoint fv (e : exp) {struct e} : atoms :=
match e with
| bvar i => empty
| fvar x => singleton x
| abs t e1 => fv e1
| app e1 e2 => (fv e1) `union` (fv e2)
end.
(** The type [atoms] represents a finite set of elements of type
[atom]. The notation for infix union is defined in the Metatheory
library.
*)
(* Demo [f_equal]
The tactic [f_equal] converts a goal of the form [f e1 = f e1'] in
to one of the form [e1 = e1'], and similarly for [f e1 e2 = f e1'
e2'], etc.
*)
Lemma f_equal_demo : forall e1 e2, e1 = e2 -> fv e1 = fv e2.
Proof.
intros e1 e2 EQ.
f_equal.
assumption.
Qed.
(* Demo [fsetdec]
The tactic [fsetdec] solves a certain class of propositions
involving finite sets. See the documentation in [FSetWeakDecide]
for a full specification.
*)
Lemma fsetdec_demo : forall (x :atom) (S : atoms),
x `in` (singleton x `union` S).
Proof.
fsetdec.
Qed.
(** Exercise [subst_fresh]
To show the ease of reasoning with these definitions, we will prove a
standard result from lambda calculus: if a variable does not
appear free in a term, then substituting for it has no effect.
HINTS: Prove this lemma by induction on [e].
- You will need to use [simpl] in many cases. You can [simpl]
everything everywhere (including hypotheses) with the
pattern [simpl in *].
- Part of this proof includes a false assumption about free
variables. Destructing this hypothesis produces a goal about
finite set membership that is solvable by [fsetdec].
*)
Lemma subst_fresh : forall (x : atom) e u,
x `notin` fv e -> [x ~> u] e = e.
Proof.
(* FILL IN HERE (and delete "Admitted") *) Admitted.
(* Take-home Demo: Prove that free variables are not introduced by
substitution.
This proof actually is very automatable ([simpl in *; auto.] takes
care of all but the fvar case), but the explicit proof below
demonstrates two parts of the finite set library. These two parts
are the tactic [destruct_notin] and the lemma [notin_union], both
defined in the module [FSetWeakNotin].
Before stepping through this proof, you should go to that module
to read about those definitions and see what other finite set
reasoning is available.
*)
Lemma subst_notin_fv : forall x y u e,
x `notin` fv e -> x `notin` fv u ->
x `notin` fv ([y ~> u]e).
Proof.
intros x y u e Fr1 Fr2.
induction e; simpl in *.
Case "bvar".
assumption.
Case "fvar".
destruct (a == y).
assumption.
simpl. assumption.
Case "abs".
apply IHe. assumption.
Case "app".
destruct_notin.
apply notin_union.
apply IHe1.
assumption.
apply IHe2.
assumption.
Qed.
(*************************************************************************)
(** * Opening *)
(*************************************************************************)
(** Opening replaces an index with a term. It corresponds to informal
substitution for a bound variable, such as in the rule for beta
reduction. Note that only "dangling" indices (those that do not
refer to any abstraction) can be opened. Opening has no effect
for terms that are locally closed.
Natural numbers are just an inductive datatype with two
constructors: [O] (as in the letter 'oh', not 'zero') and [S],
defined in Coq.Init.Datatypes. Coq allows literal natural numbers
to be written using standard decimal notation, e.g., 0, 1, 2, etc.
The notation [k == i] is the decidable equality function for
natural numbers (cf. [Coq.Peano_dec.eq_nat_dec]). This notation
is defined in the Metatheory library.
We make several simplifying assumptions in defining [open_rec].
First, we assume that the argument [u] is locally closed. This
assumption simplifies the implementation since we do not need to
shift indices in [u] when passing under a binder. Second, we
assume that this function is initially called with index zero and
that zero is the only unbound index in the term. This eliminates
the need to possibly subtract one in the case of indices.
There is no need to worry about variable capture because bound
variables are indices.
*)
Fixpoint open_rec (k : nat) (u : exp)(e : exp)
{struct e} : exp :=
match e with
| bvar i => if k == i then u else (bvar i)
| fvar x => fvar x
| abs t e1 => abs t (open_rec (S k) u e1)
| app e1 e2 => app (open_rec k u e1) (open_rec k u e2)
end.
(** Many common applications of opening replace index zero with an
expression or variable. The following definition provides a
convenient shorthand for such uses. Note that the order of
arguments is switched relative to the definition above. For
example, [(open e x)] can be read as "substitute the variable [x]
for index [0] in [e]" and "open [e] with the variable [x]."
Recall that the coercions above let us write [x] in place of
[(fvar x)].
*)
Definition open e u := open_rec 0 u e.
(** This next demo shows the operation of [open]. For example, the
locally nameless representation of the term (\y. (\x. (y x)) y) is
[abs (app (abs (app 1 0)) 0)]. To look at the body without the
outer abstraction, we need to replace the indices that refer to
that abstraction with a name. Therefore, we show that we can open
the body of the abs above with Y to produce [app (abs (app Y 0))
Y)].
*)
Lemma demo_open :
open (app (abs typ_base (app 1 0)) 0) Y =
(app (abs typ_base (app Y 0)) Y).
Proof.
Admitted.
(* HINT for demo: To show the equality of the two sides below, use the
tactics [unfold], which replaces a definition with its RHS and
reduces it to head form, and [simpl], which reduces the term the
rest of the way. Then finish up with [auto]. *)
(*************************************************************************)
(* *)
(* Stretch break (5 mins) *)
(* *)
(*************************************************************************)
(*************************************************************************)
(** * Local closure *)
(*************************************************************************)
(** Recall that [exp] admits terms that contain unbound indices. We
say that a term is locally closed when no indices appearing in it
are unbound. The proposition [lc e] holds when an expression [e]
is locally closed.
The inductive definition below formalizes local closure such that
the resulting induction principle serves as the structural
induction principle over (locally closed) expressions. In
particular, unlike induction for type [exp], there are no cases
for bound variables. Thus, the induction principle corresponds
more closely to informal practice than the one arising from the
definition of pre-terms.
*)
Inductive lc : exp -> Prop :=
| lc_var : forall (x:atom),
lc x
| lc_abs : forall (x : atom) e t,
x `notin` fv e -> lc (open e x) ->
lc (abs t e)
| lc_app : forall e1 e2,
lc e1 -> lc e2 ->
lc (app e1 e2).
Hint Constructors lc.
(*************************************************************************)
(** Properties about basic operations *)
(*************************************************************************)
(** We also define a notation for [open_rec] to make stating some of
the properties simpler. However, we don't need to use open_rec
outside of this part of the tutorial so we make it a local
notation, confined to this section. *)
Section BasicOperations.
Notation Local "{ k ~> u } t" := (open_rec k u t) (at level 67).
(** The first property we would like to show is the analogue to
[subst_fresh]: index substitution has no effect for closed terms.
Here is an initial attempt at the proof.
*)
Lemma open_rec_lc_0 : forall k u e,
lc e ->
e = {k ~> u} e.
Proof.
intros k u e LC.
induction LC.
Case "lc_fvar".
simpl.
auto.
Case "lc_abs".
simpl.
f_equal.
Admitted.
(** At this point there are two problems. Our goal is about
substitution for index [S k] in term [e], while our induction
hypothesis IHLC only tells use about index [k] in term [open e x].
To solve the first problem, we generalize our IH over all [k].
That way, when [k] is incremented in the [abs] case, it will still
apply. Below, we use the tactic [generalize dependent] to
generalize over [k] before using induction.
*)
Lemma open_rec_lc_1 : forall k u e,
lc e ->
e = {k ~> u} e.
Proof.
intros k u e LC.
generalize dependent k.
induction LC.
Case "lc_fvar".
simpl. auto.
Case "lc_abs".
simpl.
intro k.
f_equal.
Admitted.
(** At this point we are still stuck because the IH concerns
[open e x] instead of [e]. The result that we need is that if an
index substitution has no effect for an opened term, then it has
no effect for the raw term (as long as we are *not* substituting
for [0], hence [S k] below).
<<
open e x = {S k ~> u}(open e x) -> e = {S k ~> u} e
>>
In other words, expanding the definition of open:
<<
{0 ~> x}e = {S k ~> u}({0 ~> x} e) -> e = {S k ~> u} e
>>
Of course, to prove this result, we must generalize
[0] and [S k] to be any pair of inequal numbers to get a strong
enough induction hypothesis for the [abs] case.
*)
Lemma open_rec_lc_core : forall e j v i u,
i <> j ->
{j ~> v} e = {i ~> u} ({j ~> v} e) ->
e = {i ~> u} e.
Proof.
induction e; intros j v i u Neq H; simpl in *.
Case "bvar".
destruct (j == n); destruct (i == n).
SCase "j = n = i".
subst n. destruct Neq. auto.
SCase "j = n, i <> n".
auto.
SCase "j <> n, i = n".
subst n. simpl in H.
destruct (i == i).
SSCase "i=i".
auto.
SSCase "i<>i".
destruct n. auto.
SCase "j <> n, i <> n".
auto.
Case "fvar".
auto.
Case "abs".
f_equal.
inversion H.
apply IHe with (j := S j) (u := u) (i := S i) (v := v).
auto.
auto.
Case "app".
inversion H.
f_equal.
eapply IHe1; eauto.
eapply IHe2; eauto.
Qed.
(* Take-home Exercise: We've proven the above lemma very explicitly,
so that you can step through it slowly to see how it
works. However, with automation, it is possible to give a *much*
shorter proof. Reprove this lemma on your own to see how compact
you can make it. *)
(** With the help of this lemma, we can complete the proof. *)
Lemma open_rec_lc : forall k u e,
lc e -> e = {k ~> u} e.
Proof.
intros k u e LC.
generalize dependent k.
induction LC.
Case "lc_fvar".
simpl.
auto.
Case "lc_abs".
simpl.
intro k.
f_equal.
unfold open in *.
apply open_rec_lc_core with
(i := S k) (j := 0) (u := u) (v := fvar x).
auto.
auto.
Case "lc_app".
intro k.
simpl.
f_equal.
auto.
auto.
Qed.
(** Take-home Exercise [subst_open_rec] *)
(** The next lemma demonstrates that free variable substitution
distributes over index substitution.
The proof of this lemma is by straightforward induction over
[e1]. When [e1] is a free variable, we need to appeal to
[open_rec_lc], proved above.
*)
Lemma subst_open_rec : forall e1 e2 u (x : atom) k,
lc u ->
[x ~> u] ({k ~> e2} e1) = {k ~> [x ~> u] e2} ([x ~> u] e1).
Proof.
(* OPTIONAL EXERCISE *) Admitted.
(** *** Exercise [subst_open_var] *)
(** The lemma above is most often used with [k = 0] and
[e2] as some fresh variable. Therefore, it simplifies matters
to define the following useful corollary.
HINT: Do not use induction.
Rewrite with [subst_open_rec] and [subst_neq_var].
*)
Lemma subst_open_var : forall (x y : atom) u e,
y <> x ->
lc u ->
open ([x ~> u] e) y = [x ~> u] (open e y).
Proof.
(* FILL IN HERE (and delete "Admitted") *) Admitted.
(** *** Take-home Exercise [subst_intro] *)
(** This lemma states that opening can be replaced with variable
opening and substitution.
HINT: Prove by induction on [e], first generalizing the
argument to [open_rec] by using the [generalize] tactic, e.g.,
[generalize 0].
*)
Lemma subst_intro : forall (x : atom) u e,
x `notin` (fv e) ->
open e u = [x ~> u](open e x).
Proof.
(* OPTIONAL EXERCISE *) Admitted.
End BasicOperations.
(*************************************************************************)
(** Cofinite quantification *)
(*************************************************************************)
(* In the next example, we will reexamine the definition of
[lc] in the [abs] case.
The lemma [subst_lc] says that local closure is preserved by
substitution. Let's start working through this proof.
*)
Lemma subst_lc_0 : forall (x : atom) u e,
lc e ->
lc u ->
lc ([x ~> u] e).
Proof.
intros x u e He Hu.
induction He.
Case "lc_fvar".
simpl.
destruct (x0 == x).
auto.
auto.
Case "lc_abs".
simpl.
Print lc_abs.
apply lc_abs with (x:=x0).
apply subst_notin_fv.
auto.
Admitted.
(** Here we are stuck. We don't know that [x0] is not in the free
variables of [u].
The solution is to change the *definition* of local closure so
that we get a different induction principle. Currently, in the
[lc_abs] case, we show that an abstraction is locally closed by
showing that the body is locally closed after it has been opened
with one particular variable.
<<
| lc_abs : forall (x : atom) e,
x `notin` fv e ->
lc (open e x) ->
lc (abs e)
>>
Therefore, our induction hypothesis in this case only applies to
that variable. From the hypothesis list in the [lc_abs] case:
x0 : atom,
IHHe : lc ([x ~> u]open e x0)
The problem is that we don't have any assumptions about [x0]. It
could very well be equal to [x].
A stronger induction principle provides an IH that applies to many
variables. In that case, we could pick one that is "fresh enough".
To do so, we need to revise the above definition of lc and replace
the type of lc_abs with this one:
<<
| lc_abs_c : forall L e,
(forall x:atom, x `notin` L -> lc (open e x)) ->
lc (abs e)
>>
This rule says that to show that an abstraction is locally closed,
we need to show that the body is closed, after it has been opened by
any atom [x], *except* those in some set [L]. With this rule, the IH
in this proof is now:
H0 : forall x0 : atom, x0 `notin` L -> lc ([x ~> u]open e x0)
Below, lc_c is the local closure judgment revised to use this new
rule in the abs case. We call this "cofinite quantification"
because the IH applies to an infinite number of atoms [x0], except
those in some finite set [L].
Changing the rule in this way does not change what terms are locally
closed. (For more details about cofinite-quantification see:
"Engineering Formal Metatheory", Aydemir, Chargu'eraud, Pierce,
Pollack, Weirich. POPL 2008.)
*)
Inductive lc_c : exp -> Prop :=
| lc_var_c : forall (x:atom),
lc_c x
| lc_abs_c : forall (L : atoms) e T,
(forall x : atom, x `notin` L -> lc_c (open e x)) ->
lc_c (abs T e)
| lc_app_c : forall e1 e2,
lc_c e1 ->
lc_c e2 ->
lc_c (app e1 e2).
Hint Constructors lc_c.
(* Reintroduce notation for [open_rec] so that we can reprove
properties about it and the new version of lc_c. *)
Section CofiniteQuantification.
Notation Local "{ k ~> u } t" := (open_rec k u t) (at level 67).
(* With this new definition, we can almost use the same proof for
[open_rec_lc], we only need one change. We need to add the line
[pick fresh x for L.] immediately before the use of [apply
open_rec_lc_core]. This tactic, defined in [Metatheory],
introduces a new atom [x] that is known not to be in the set [L].
*)
Lemma open_rec_lc_c : forall k u e,
lc_c e ->
e = {k ~> u} e.
Proof.
intros k u e LC.
generalize dependent k.
induction LC.
Case "lc_fvar".
simpl.
auto.
Case "lc_abs".
simpl.
intro k.
f_equal.
unfold open in *.
pick fresh x for L. (* Note: NEW LINE added *)
apply open_rec_lc_core with
(i := S k) (j := 0) (u := u) (v := fvar x).
auto.
auto.
Case "lc_app".
intro k.
simpl.
f_equal.
auto.
auto.
Qed.
(* Take-home Exercise: The next two lemmas have exactly the same
proofs as before. *)
Lemma subst_open_rec_c : forall e1 e2 u (x : atom) k,
lc_c u ->
[x ~> u] ({k ~> e2} e1) = {k ~> [x ~> u] e2} ([x ~> u] e1).
Proof.
(* OPTIONAL EXERCISE *) Admitted.
Lemma subst_open_var_c : forall (x y : atom) u e,
y <> x ->
lc_c u ->
open ([x ~> u] e) y = [x ~> u] (open e y).
Proof.
(* OPTIONAL EXERCISE *) Admitted.
(* Exercise [subst_lc_c]:
Once we have changed the definition of lc, we can complete the
proof of subst_lc.
HINT: apply lc_abs_c with cofinite set (L `union` singleton x).
This gives us an atom x0, and a hypothesis that
x0 is fresh for both L and x.
*)
Lemma subst_lc_c : forall (x : atom) u e,
lc_c e ->
lc_c u ->
lc_c ([x ~> u] e).
Proof.
intros x u e He Hu.
induction He.
Case "lc_var_c".
simpl.
destruct (x0 == x).
auto.
auto.
Case "lc_abs_c".
simpl.
(* FILL IN HERE (and delete "Admitted") *) Admitted.
End CofiniteQuantification.
(*************************************************************************)
(** * Tactic support *)
(*************************************************************************)
(** When picking a fresh atom or applying a rule that uses cofinite
quantification, choosing a set of atoms to be fresh for can be
tedious. In practice, it is simpler to use a tactic to choose the
set to be as large as possible.
The tactic [gather_atoms] is used to collect together all the
atoms in the context. It relies on an auxiliary tactic,
[gather_atoms_with] (from MetatheoryAtom), which maps a function
that returns a finite set of atoms over all hypotheses with the
appropriate type.
*)
Ltac gather_atoms ::=
let A := gather_atoms_with (fun x : atoms => x) in
let B := gather_atoms_with (fun x : atom => singleton x) in
let C := gather_atoms_with (fun x : list (atom * typ) => dom x) in
let D := gather_atoms_with (fun x : exp => fv x) in
constr:(A `union` B `union` C `union` D).
(** A number of other, useful tactics are defined by the Metatheory
library, and each depends on [gather_atoms]. By redefining
[gather_atoms], denoted by the [::=] in its definition below, we
automatically update these tactics so that they use the proper
notion of "all atoms in the context."
For example, the tactic [(pick fresh x)] chooses an atom fresh for
"everything" in the context. It is the same as [(pick fresh x for
L)], except where [L] has been computed by [gather_atoms].
The tactic [(pick fresh x and apply H)] applies a rule [H] that is
defined using cofinite quantification. It automatically
instantiates the finite set of atoms to exclude using
[gather_atoms].
*)
(** *** Example
Below, we reprove [subst_lc_c] using [(pick fresh and apply)].
Step through the proof below to see how [(pick fresh and apply)]
works.
*)
Lemma subst_lc_c_alternate_proof : forall (x : atom) u e,
lc_c e ->
lc_c u ->
lc_c ([x ~> u] e).
Proof.
intros x u e He Hu.
induction He.
Case "fvar".
simpl.
destruct (x0 == x).
auto.
auto.
Case "abs".
simpl.
pick fresh y and apply lc_abs_c.
(* Here, take note of the hypothesis [Fr]. *)
rewrite subst_open_var_c. auto. auto. auto.
Case "app".
simpl. auto.
Qed.
(*************************************************************************)
(* *)
(* Coffee break *)
(* *)
(*************************************************************************)
(*************************************************************************)
(** * Typing environments *)
(*************************************************************************)
(** We represent environments as association lists (lists of pairs of
keys and values) whose keys are [atom]s.
*)
Notation env := (list (atom * typ)).
(** For STLC, environments bind [atom]s to [typ]s. We define an
abbreviation [env] for the type of these environments. Coq will
print [list (atom * typ)] as [env], and we can use [env] as a
shorthand for writing [list (atom * typ)].
Lists are defined in Coq's standard library, with the constructors
[nil] and [cons]. The list library includes the [::] notation
for cons as well as standard list operations such as append, map,
and fold. The infix operation "++" is list append.
The Metatheory library extends this reasoning by instantiating the
AssocList library to provide support for association lists whose
keys are [atom]s. Everything in this library is polymorphic over
the type of objects bound in the environment. Look in AssocList
for additional details about the functions and predicates that we
mention below.
*)
(** Environment equality *)
(** When reasoning about environments, we often need to talk about
bindings in the "middle" of an environment. Therefore, it is common
for lemmas and definitions to use list append in their statements.
Unfortunately, list append is associative, so two Coq expressions may
denote the same environment even though they are not equal.
The tactic [simpl_env] reassociates all concatenations of
environments to the right.
*)
Lemma append_assoc_demo : forall (E0 E1 E2 E3:env),
E0 ++ (E1 ++ E2) ++ E3 = E0 ++ E1 ++ E2 ++ E3.
Proof.
intros.
auto. (* Does nothing. *)
simpl_env.
reflexivity.
Qed.
(** To make environments easy to read, instead of building them from
[nil] and [cons], we prefer to build them from the following
components:
- [nil]: The empty list.
- [one]: Lists consisting of exactly one item.
- [++]: List append.
Furthermore, we introduce compact notation for one (singleton lists):
[(x ~ T)] is the same as [one (x, T)].
*)
(** The simpl_env tactic actually puts lists built from only nil, one
and [++] into a "normal form". This process reassociates all appends
to the right, removes extraneous nils converts cons to singleton
lists with an append.
*)
Lemma simpl_env_demo : forall (x y:atom) (T1 T2:typ) (E F:env),
((x ~ T1) ++ nil) ++ (y,T2) :: (nil ++ E) ++ F =
(x ~ T1) ++ (y ~ T2) ++ E ++ F.
Proof.
intros.
(* simpl_env puts the left side into the normal form. *)
simpl_env.
reflexivity.
Qed.
(** Note that the [simpl] tactic doesn't produce the "normal form" for
environments. It should always be followed up with [simpl_env].