(* ################################################################# *)
(** * 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) := id -> A.(** Intuitively, a total map over an element type [A] is just a
    function that can be used to look up [id]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 id. *)

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 : id) (v : A) :=
  fun x' => if beq_id 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 [id]s to [bool]s, where [Id
    3] is mapped to [true] and every other key is mapped to [false],
    like this: *)

Definition examplemap :=
  t_update (t_update (t_empty false) (Id "foo") false)
           (Id "bar") true.(** 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 (Id "baz") = false.Example update_example2 : examplemap (Id "foo") = false.Example update_example3 : examplemap (Id "quux") = false.Example update_example4 : examplemap (Id "bar") = true.

(* Specifications: *)
Lemma t_apply_empty: forall A x v, @t_empty A v x = v.
Lemma t_update_eq : forall A (m: total_map A) x v, (t_update m x v) x = v.
Theorem t_update_neq : forall (X:Type) v x1 x2 (m : total_map X), x1 <> x2 -> (t_update m x1 v) x2 = m x2.
Lemma t_update_shadow : forall A (m: total_map A) v1 v2 x, t_update (t_update m x v1) x v2 = t_update m x v2.
Lemma beq_idP : forall x y, reflect (x = y) (beq_id x y).
Theorem t_update_same : forall X x (m : total_map X), t_update m x (m x) = m.
Theorem t_update_permute : forall (X:Type) v1 v2 x1 x2 (m : total_map X), x2 <> x1 -> (t_update (t_update m x2 v2) x1 v1) = (t_update (t_update m x1 v1) x2 v2).