-
Notifications
You must be signed in to change notification settings - Fork 0
/
Maps.v
424 lines (337 loc) · 13.9 KB
/
Maps.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
(** * Maps: Total and Partial Maps *)
(** _Maps_ (or _dictionaries_) are ubiquitous data structures both
generally and in the theory of programming languages in
particular; we're going to need them in many places in the coming
chapters. They also make a nice case study using ideas we've seen
in previous chapters, including building data structures out of
higher-order functions (from [Basics] and [Poly]) and the use of
reflection to streamline proofs (from [IndProp]).
We'll define two flavors of maps: _total_ maps, which include a
"default" element to be returned when a key being looked up
doesn't exist, and _partial_ maps, which return an [option] to
indicate success or failure. The latter is defined in terms of
the former, using [None] as the default element. *)
(* ################################################################# *)
(** * The Coq Standard Library *)
(** One small digression before we begin...
Unlike the chapters we have seen so far, this one does not
[Require Import] the chapter before it (and, transitively, all the
earlier chapters). Instead, in this chapter and from now, on
we're going to import the definitions and theorems we need
directly from Coq's standard library stuff. You should not notice
much difference, though, because we've been careful to name our
own definitions and theorems the same as their counterparts in the
standard library, wherever they overlap. *)
From Coq Require Import Arith.Arith.
From Coq Require Import Bool.Bool.
Require Export Coq.Strings.String.
From Coq Require Import Logic.FunctionalExtensionality.
From Coq Require Import Lists.List.
Import ListNotations.
(** Documentation for the standard library can be found at
http://coq.inria.fr/library/.
The [Search] command is a good way to look for theorems involving
objects of specific types. Take a minute now to experiment with it. *)
(* ################################################################# *)
(** * Identifiers *)
(** First, we need a type for the keys that we use to index into our
maps. In [Lists.v] we introduced a fresh type [id] for a similar
purpose; here and for the rest of _Software Foundations_ we will
use the [string] type from Coq's standard library. *)
(** To compare strings, we define the function [eqb_string], which
internally uses the function [string_dec] from Coq's string
library. *)
Definition eqb_string (x y : string) : bool :=
if string_dec x y then true else false.
(** (The function [string_dec] comes from Coq's string library.
If you check the result type of [string_dec], you'll see that it
does not actually return a [bool], but rather a type that looks
like [{x = y} + {x <> y}], called a [sumbool], which can be
thought of as an "evidence-carrying boolean." Formally, an
element of [sumbool] is either a proof that two things are equal
or a proof that they are unequal, together with a tag indicating
which. But for present purposes you can think of it as just a
fancy [bool].) *)
(** Now we need a few basic properties of string equality... *)
Theorem eqb_string_refl : forall s : string, true = eqb_string s s.
Proof. intros s. unfold eqb_string. destruct (string_dec s s) as [|Hs].
- reflexivity.
- destruct Hs. reflexivity.
Qed.
(** The following useful property follows from an analogous
lemma about strings: *)
Theorem eqb_string_true_iff : forall x y : string,
eqb_string x y = true <-> x = y.
Proof.
intros x y.
unfold eqb_string.
destruct (string_dec x y) as [|Hs].
- subst. split. reflexivity. reflexivity.
- split.
+ intros contra. discriminate contra.
+ intros H. rewrite H in Hs. destruct Hs. reflexivity.
Qed.
(** Similarly: *)
Theorem eqb_string_false_iff : forall x y : string,
eqb_string x y = false <-> x <> y.
Proof.
intros x y. rewrite <- eqb_string_true_iff.
rewrite not_true_iff_false. reflexivity. Qed.
(** This handy variant follows just by rewriting: *)
Theorem false_eqb_string : forall x y : string,
x <> y -> eqb_string x y = false.
Proof.
intros x y. rewrite eqb_string_false_iff.
intros H. apply H. Qed.
(* ################################################################# *)
(** * Total Maps *)
(** Our main job in this chapter will be to build a definition of
partial maps that is similar in behavior to the one we saw in the
[Lists] chapter, plus accompanying lemmas about its behavior.
This time around, though, we're going to use _functions_, rather
than lists of key-value pairs, to build maps. The advantage of
this representation is that it offers a more _extensional_ view of
maps, where two maps that respond to queries in the same way will
be represented as literally the same thing (the very same function),
rather than just "equivalent" data structures. This, in turn,
simplifies proofs that use maps. *)
(** We build partial maps in two steps. First, we define a type of
_total maps_ that return a default value when we look up a key
that is not present in the map. *)
Definition total_map (A : Type) := string -> A.
(** Intuitively, a total map over an element type [A] is just a
function that can be used to look up [string]s, yielding [A]s. *)
(** The function [t_empty] yields an empty total map, given a default
element; this map always returns the default element when applied
to any string. *)
Definition t_empty {A : Type} (v : A) : total_map A :=
(fun _ => v).
(** More interesting is the [update] function, which (as before) takes
a map [m], a key [x], and a value [v] and returns a new map that
takes [x] to [v] and takes every other key to whatever [m] does. *)
Definition t_update {A : Type} (m : total_map A)
(x : string) (v : A) :=
fun x' => if eqb_string x x' then v else m x'.
(** This definition is a nice example of higher-order programming:
[t_update] takes a _function_ [m] and yields a new function
[fun x' => ...] that behaves like the desired map. *)
(** For example, we can build a map taking [string]s to [bool]s, where
["foo"] and ["bar"] are mapped to [true] and every other key is
mapped to [false], like this: *)
Definition examplemap :=
t_update (t_update (t_empty false) "foo" true)
"bar" true.
(** Next, let's introduce some new notations to facilitate working
with maps. *)
(** First, we will use the following notation to create an empty
total map with a default value. *)
Notation "'_' '!->' v" := (t_empty v)
(at level 100, right associativity).
Example example_empty := (_ !-> false).
(** We then introduce a convenient notation for extending an existing
map with some bindings. *)
Notation "x '!->' v ';' m" := (t_update m x v)
(at level 100, v at next level, right associativity).
(** The [examplemap] above can now be defined as follows: *)
Definition examplemap' :=
( "bar" !-> true;
"foo" !-> true;
_ !-> false
).
(** This completes the definition of total maps. Note that we
don't need to define a [find] operation because it is just
function application! *)
Example update_example1 : examplemap' "baz" = false.
Proof. reflexivity. Qed.
Example update_example2 : examplemap' "foo" = true.
Proof. reflexivity. Qed.
Example update_example3 : examplemap' "quux" = false.
Proof. reflexivity. Qed.
Example update_example4 : examplemap' "bar" = true.
Proof. reflexivity. Qed.
(** To use maps in later chapters, we'll need several fundamental
facts about how they behave. *)
(** Even if you don't work the following exercises, make sure
you thoroughly understand the statements of the lemmas! *)
(** (Some of the proofs require the functional extensionality axiom,
which is discussed in the [Logic] chapter.) *)
(** **** Exercise: 1 star, standard, optional (t_apply_empty)
First, the empty map returns its default element for all keys: *)
Lemma t_apply_empty : forall (A : Type) (x : string) (v : A),
(_ !-> v) x = v.
Proof.
intros. unfold t_empty. auto.
Qed.
(** [] *)
(** **** Exercise: 2 stars, standard, optional (t_update_eq)
Next, if we update a map [m] at a key [x] with a new value [v]
and then look up [x] in the map resulting from the [update], we
get back [v]: *)
Lemma t_update_eq : forall (A : Type) (m : total_map A) x v,
(x !-> v ; m) x = v.
Proof.
intros.
unfold t_update.
assert (true = eqb_string x x).
apply eqb_string_refl.
replace (eqb_string x x) with true.
auto.
Qed.
(** [] *)
(** **** Exercise: 2 stars, standard, optional (t_update_neq)
On the other hand, if we update a map [m] at a key [x1] and then
look up a _different_ key [x2] in the resulting map, we get the
same result that [m] would have given: *)
Theorem t_update_neq : forall (A : Type) (m : total_map A) x1 x2 v,
x1 <> x2 ->
(x1 !-> v ; m) x2 = m x2.
Proof.
intros.
unfold t_update.
assert (eqb_string x1 x2 = false).
apply eqb_string_false_iff.
auto.
replace (eqb_string x1 x2) with false.
auto.
Qed.
(** [] *)
(** **** Exercise: 2 stars, standard, optional (t_update_shadow)
If we update a map [m] at a key [x] with a value [v1] and then
update again with the same key [x] and another value [v2], the
resulting map behaves the same (gives the same result when applied
to any key) as the simpler map obtained by performing just
the second [update] on [m]: *)
Lemma t_update_shadow : forall (A : Type) (m : total_map A) x v1 v2,
(x !-> v2 ; x !-> v1 ; m) = (x !-> v2 ; m).
Proof.
intros.
unfold t_update.
apply functional_extensionality_dep; intros.
destruct (eqb_string x x0); auto.
Qed.
(** [] *)
(** For the final two lemmas about total maps, it's convenient to use
the reflection idioms introduced in chapter [IndProp]. We begin
by proving a fundamental _reflection lemma_ relating the equality
proposition on [id]s with the boolean function [eqb_id]. *)
(** **** Exercise: 2 stars, standard, optional (eqb_stringP)
Use the proof of [eqbP] in chapter [IndProp] as a template to
prove the following: *)
Lemma eqb_stringP : forall x y : string,
reflect (x = y) (eqb_string x y).
Proof.
intros.
apply iff_reflect.
split; intros.
rewrite H.
symmetry.
apply eqb_string_refl.
apply eqb_string_true_iff; auto.
Qed.
(** [] *)
(** Now, given [string]s [x1] and [x2], we can use the tactic
[destruct (eqb_stringP x1 x2)] to simultaneously perform case
analysis on the result of [eqb_string x1 x2] and generate
hypotheses about the equality (in the sense of [=]) of [x1]
and [x2]. *)
(** **** Exercise: 2 stars, standard (t_update_same)
With the example in chapter [IndProp] as a template, use
[eqb_stringP] to prove the following theorem, which states that
if we update a map to assign key [x] the same value as it already
has in [m], then the result is equal to [m]: *)
Theorem t_update_same : forall (A : Type) (m : total_map A) x,
(x !-> m x ; m) = m.
Proof.
intros.
unfold t_update.
apply functional_extensionality_dep; intros.
destruct (eqb_stringP x x0); auto.
replace x with x0; auto.
Qed.
(** [] *)
(** **** Exercise: 3 stars, standard, recommended (t_update_permute)
Use [eqb_stringP] to prove one final property of the [update]
function: If we update a map [m] at two distinct keys, it doesn't
matter in which order we do the updates. *)
Theorem t_update_permute : forall (A : Type) (m : total_map A)
v1 v2 x1 x2,
x2 <> x1 ->
(x1 !-> v1 ; x2 !-> v2 ; m)
=
(x2 !-> v2 ; x1 !-> v1 ; m).
Proof.
intros.
unfold t_update.
apply functional_extensionality_dep; intros.
destruct (eqb_stringP x1 x).
destruct (eqb_stringP x2 x).
rewrite <- e in e0.
rewrite e0 in H.
exfalso.
apply H; auto.
auto.
destruct (eqb_string x2 x); auto.
Qed.
(** [] *)
(* ################################################################# *)
(** * Partial maps *)
(** Finally, we define _partial maps_ on top of total maps. A partial
map with elements of type [A] is simply a total map with elements
of type [option A] and default element [None]. *)
Definition partial_map (A : Type) := total_map (option A).
Definition empty {A : Type} : partial_map A :=
t_empty None.
Definition update {A : Type} (m : partial_map A)
(x : string) (v : A) :=
(x !-> Some v ; m).
(** We introduce a similar notation for partial maps: *)
Notation "x '|->' v ';' m" := (update m x v)
(at level 100, v at next level, right associativity).
(** We can also hide the last case when it is empty. *)
Notation "x '|->' v" := (update empty x v)
(at level 100).
Example examplepmap :=
("Church" |-> true ; "Turing" |-> false).
(** We now straightforwardly lift all of the basic lemmas about total
maps to partial maps. *)
Lemma apply_empty : forall (A : Type) (x : string),
@empty A x = None.
Proof.
intros. unfold empty. rewrite t_apply_empty.
reflexivity.
Qed.
Lemma update_eq : forall (A : Type) (m : partial_map A) x v,
(x |-> v ; m) x = Some v.
Proof.
intros. unfold update. rewrite t_update_eq.
reflexivity.
Qed.
Theorem update_neq : forall (A : Type) (m : partial_map A) x1 x2 v,
x2 <> x1 ->
(x2 |-> v ; m) x1 = m x1.
Proof.
intros A m x1 x2 v H.
unfold update. rewrite t_update_neq. reflexivity.
apply H. Qed.
Lemma update_shadow : forall (A : Type) (m : partial_map A) x v1 v2,
(x |-> v2 ; x |-> v1 ; m) = (x |-> v2 ; m).
Proof.
intros A m x v1 v2. unfold update. rewrite t_update_shadow.
reflexivity.
Qed.
Theorem update_same : forall (A : Type) (m : partial_map A) x v,
m x = Some v ->
(x |-> v ; m) = m.
Proof.
intros A m x v H. unfold update. rewrite <- H.
apply t_update_same.
Qed.
Theorem update_permute : forall (A : Type) (m : partial_map A)
x1 x2 v1 v2,
x2 <> x1 ->
(x1 |-> v1 ; x2 |-> v2 ; m) = (x2 |-> v2 ; x1 |-> v1 ; m).
Proof.
intros A m x1 x2 v1 v2. unfold update.
apply t_update_permute.
Qed.
(* Thu Feb 7 20:09:22 EST 2019 *)