MapsTotal and Partial Maps
Require Import Coq.Arith.Arith.
Require Import Coq.Bool.Bool.
Require Export Coq.Strings.String.
Require Import Coq.Logic.FunctionalExtensionality.
Require Import Coq.Lists.List.
Import ListNotations.
Require Import Coq.Bool.Bool.
Require Export Coq.Strings.String.
Require Import Coq.Logic.FunctionalExtensionality.
Require Import Coq.Lists.List.
Import ListNotations.
Identifiers
Print string.
Print Ascii.ascii.
Open Scope string_scope.
Definition string_ex' := "I'm a string!".
Print Ascii.ascii.
Open Scope string_scope.
Definition string_ex' := "I'm a string!".
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 :=
if string_dec x y then true else false.
if string_dec x y then true else false.
Theorem eqb_string_refl : ∀ s, true = eqb_string s s.
Theorem eqb_string_true_iff : ∀ x y : string,
eqb_string x y = true ↔ x = y.
Theorem eqb_string_false_iff : ∀ x y : string,
eqb_string x y = false
↔ x ≠ y.
Theorem eqb_string_true_iff : ∀ x y : string,
eqb_string x y = true ↔ x = y.
Theorem eqb_string_false_iff : ∀ x y : string,
eqb_string x y = false
↔ x ≠ y.
Total 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 strings, yielding As.
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).
(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'.
(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 strings to bools, 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.
t_update (t_update (t_empty false) "foo" true)
"bar" true.
Next, let's introduce some new notations to facilitate working with maps.
Notation "{ --> d }" := (t_empty d) (at level 0).
Notation "m '&' { a --> x }" :=
(t_update m a x) (at level 20).
Notation "m '&' { a --> x ; b --> y }" :=
(t_update (m & { a --> x }) b y) (at level 20).
Notation "m '&' { a --> x ; b --> y ; c --> z }" :=
(t_update (m & { a --> x ; b --> y }) c z) (at level 20).
Notation "m '&' { a --> x ; b --> y ; c --> z ; d --> t }" :=
(t_update (m & { a --> x ; b --> y ; c --> z }) d t) (at level 20).
Notation "m '&' { a --> x ; b --> y ; c --> z ; d --> t ; e --> u }" :=
(t_update (m & { a --> x ; b --> y ; c --> z ; d --> t }) e u) (at level 20).
Notation "m '&' { a --> x ; b --> y ; c --> z ; d --> t ; e --> u ; f --> v }" :=
(t_update (m & { a --> x ; b --> y ; c --> z ; d --> t ; e --> u }) f v) (at level 20).
(t_update m a x) (at level 20).
Notation "m '&' { a --> x ; b --> y }" :=
(t_update (m & { a --> x }) b y) (at level 20).
Notation "m '&' { a --> x ; b --> y ; c --> z }" :=
(t_update (m & { a --> x ; b --> y }) c z) (at level 20).
Notation "m '&' { a --> x ; b --> y ; c --> z ; d --> t }" :=
(t_update (m & { a --> x ; b --> y ; c --> z }) d t) (at level 20).
Notation "m '&' { a --> x ; b --> y ; c --> z ; d --> t ; e --> u }" :=
(t_update (m & { a --> x ; b --> y ; c --> z ; d --> t }) e u) (at level 20).
Notation "m '&' { a --> x ; b --> y ; c --> z ; d --> t ; e --> u ; f --> v }" :=
(t_update (m & { a --> x ; b --> y ; c --> z ; d --> t ; e --> u }) f v) (at level 20).
Definition examplemap' :=
{ --> false } & { "foo" --> true ; "bar" --> true }.
{ --> false } & { "foo" --> true ; "bar" --> true }.
To use maps in later chapters, we'll need several fundamental facts about how they behave.
Lemma t_apply_empty: ∀ (A:Type) (x: string) (v: A), { --> v } x = v.
Lemma t_update_eq : ∀ A (m: total_map A) x v,
(m & {x --> v}) x = v.
Theorem t_update_neq : ∀ (X:Type) v x1 x2
(m : total_map X),
x1 ≠ x2 →
(m & {x1 --> v}) x2 = m x2.
Lemma t_update_shadow : ∀ A (m: total_map A) v1 v2 x,
m & {x --> v1 ; x --> v2} = m & {x --> v2}.
Proof.
(* FILL IN HERE *) Admitted.
(* FILL IN HERE *) Admitted.
Lemma t_update_eq : ∀ A (m: total_map A) x v,
(m & {x --> v}) x = v.
Proof.
(* FILL IN HERE *) Admitted.
(* FILL IN HERE *) Admitted.
Theorem t_update_neq : ∀ (X:Type) v x1 x2
(m : total_map X),
x1 ≠ x2 →
(m & {x1 --> v}) x2 = m x2.
Proof.
(* FILL IN HERE *) Admitted.
(* FILL IN HERE *) Admitted.
Lemma t_update_shadow : ∀ A (m: total_map A) v1 v2 x,
m & {x --> v1 ; x --> v2} = m & {x --> v2}.
Proof.
(* FILL IN HERE *) Admitted.
(* FILL IN HERE *) Admitted.
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 ids with the boolean function eqb_id.
Lemma eqb_stringP : ∀ x y, reflect (x = y) (eqb_string x y).
Proof.
(* FILL IN HERE *) Admitted.
(* FILL IN HERE *) Admitted.
Now, given strings x1 and x2, we can use the 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.
Theorem t_update_same : ∀ X x (m : total_map X),
m & { x --> m x } = m.
Proof.
(* FILL IN HERE *) Admitted.
(* FILL IN HERE *) Admitted.
Theorem t_update_permute : ∀ (X:Type) v1 v2 x1 x2
(m : total_map X),
x2 ≠ x1 →
m & { x2 --> v2 ; x1 --> v1 }
= m & { x1 --> v1 ; x2 --> v2 }.
Proof.
(* FILL IN HERE *) Admitted.
(* FILL IN HERE *) Admitted.
Partial maps
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) :=
m & { x --> (Some v) }.
Definition empty {A:Type} : partial_map A :=
t_empty None.
Definition update {A:Type} (m : partial_map A)
(x : string) (v : A) :=
m & { x --> (Some v) }.
Notation "m '&' {{ a --> x }}" :=
(update m a x) (at level 20).
Notation "m '&' {{ a --> x ; b --> y }}" :=
(update (m & {{ a --> x }}) b y) (at level 20).
Notation "m '&' {{ a --> x ; b --> y ; c --> z }}" :=
(update (m & {{ a --> x ; b --> y }}) c z) (at level 20).
Notation "m '&' {{ a --> x ; b --> y ; c --> z ; d --> t }}" :=
(update (m & {{ a --> x ; b --> y ; c --> z }}) d t) (at level 20).
Notation "m '&' {{ a --> x ; b --> y ; c --> z ; d --> t ; e --> u }}" :=
(update (m & {{ a --> x ; b --> y ; c --> z ; d --> t }}) e u) (at level 20).
Notation "m '&' {{ a --> x ; b --> y ; c --> z ; d --> t ; e --> u ; f --> v }}" :=
(update (m & {{ a --> x ; b --> y ; c --> z ; d --> t ; e --> u }}) f v) (at level 20).
(update m a x) (at level 20).
Notation "m '&' {{ a --> x ; b --> y }}" :=
(update (m & {{ a --> x }}) b y) (at level 20).
Notation "m '&' {{ a --> x ; b --> y ; c --> z }}" :=
(update (m & {{ a --> x ; b --> y }}) c z) (at level 20).
Notation "m '&' {{ a --> x ; b --> y ; c --> z ; d --> t }}" :=
(update (m & {{ a --> x ; b --> y ; c --> z }}) d t) (at level 20).
Notation "m '&' {{ a --> x ; b --> y ; c --> z ; d --> t ; e --> u }}" :=
(update (m & {{ a --> x ; b --> y ; c --> z ; d --> t }}) e u) (at level 20).
Notation "m '&' {{ a --> x ; b --> y ; c --> z ; d --> t ; e --> u ; f --> v }}" :=
(update (m & {{ a --> x ; b --> y ; c --> z ; d --> t ; e --> u }}) f v) (at level 20).
Lemma apply_empty : ∀ (A: Type) (x: string), @empty A x = None.
Lemma update_eq : ∀ A (m: partial_map A) x v,
(m & {{ x --> v }}) x = Some v.
Theorem update_neq : ∀ (X:Type) v x1 x2
(m : partial_map X),
x2 ≠ x1 →
(m & {{ x2 --> v }}) x1 = m x1.
Lemma update_shadow : ∀ A (m: partial_map A) v1 v2 x,
m & {{ x --> v1 ; x --> v2 }} = m & {{x --> v2}}.
Theorem update_same : ∀ X v x (m : partial_map X),
m x = Some v →
m & {{x --> v}} = m.
Theorem update_permute : ∀ (X:Type) v1 v2 x1 x2
(m : partial_map X),
x2 ≠ x1 →
m & {{x2 --> v2 ; x1 --> v1}}
= m & {{x1 --> v1 ; x2 --> v2}}.
Lemma update_eq : ∀ A (m: partial_map A) x v,
(m & {{ x --> v }}) x = Some v.
Theorem update_neq : ∀ (X:Type) v x1 x2
(m : partial_map X),
x2 ≠ x1 →
(m & {{ x2 --> v }}) x1 = m x1.
Lemma update_shadow : ∀ A (m: partial_map A) v1 v2 x,
m & {{ x --> v1 ; x --> v2 }} = m & {{x --> v2}}.
Theorem update_same : ∀ X v x (m : partial_map X),
m x = Some v →
m & {{x --> v}} = m.
Theorem update_permute : ∀ (X:Type) v1 v2 x1 x2
(m : partial_map X),
x2 ≠ x1 →
m & {{x2 --> v2 ; x1 --> v1}}
= m & {{x1 --> v1 ; x2 --> v2}}.