FiniteMap
A finite map from A
into B
(finite_map A B
) is a mapping from
A
to option B
, in which only finitely many arguments map to
anything other than None
.
finite_map : intersect (i : level) . U i -> U i -> U i
The operations on a finite map are lookup
, empty
, update
,
remove
, and merge
:
lookup : intersect (i : level) . forall (A B : U i) . finite_map A B -> A -> option B
(2 implicit arguments)
empty : intersect (i : level) . forall (A B : U i) . eqtest A -> finite_map A B
(2 implicit arguments)
update : intersect (i : level) .
forall (A B : U i) . finite_map A B -> A -> B -> finite_map A B
(2 implicit arguments)
remove : intersect (i : level) . forall (A B : U i) . finite_map A B -> A -> finite_map A B
(2 implicit arguments)
merge : intersect (i : level) .
forall (A B : U i) . finite_map A B -> finite_map A B -> finite_map A B
(2 implicit arguments)
The update operation update f a b
produces a new finite map that
maps a
to Some b
. If a
was already in f
’s domain, it
overwrites a
’s old mapping. The merge operation merge f g
combines the two finite maps into one; if an argument is in both
domains, the left-hand mapping takes precedence.
To build a finite map requires the domain type to have decidable
equality. The equality test is supplied to the empty
operation.
eqtest : intersect (i : level) . U i -> U i
= fn A . { e : A -> A -> bool | forall (x y : A) . x = y : A <-> Bool.istrue (e x y) }
A recommended way to use empty
is to define eqt : eqtest A
, then
define myempty
to be empty eqt
. If myempty
is then made a soft
constant, the lemmas that follow will work
with myempty
without any additional effort.
Finite maps have extensional equality:
finite_map_ext : forall (i : level) (A B : U i) (f g : finite_map A B) .
(forall (a : A) . lookup f a = lookup g a : option B)
-> f = g : finite_map A B
Several principles determine how the operations interact with lookup:
lookup_empty : forall (i : level) (A B : U i) (e : eqtest A) (a : A) .
lookup (empty e) a = None : option B
lookup_update : forall (i : level) (A B : U i) (f : finite_map A B) (a : A) (b : B) .
lookup (update f a b) a = Some b : option B
lookup_update_neq : forall
(i : level)
(A B : U i)
(f : finite_map A B)
(a : A)
(b : B)
(a' : A) .
a != a' : A -> lookup (update f a b) a' = lookup f a' : option B
lookup_remove : forall (i : level) (A B : U i) (f : finite_map A B) (a : A) .
lookup (remove f a) a = None : option B
lookup_remove_neq : forall (i : level) (A B : U i) (f : finite_map A B) (a a' : A) .
a != a' : A -> lookup (remove f a) a' = lookup f a' : option B
lookup_merge_left : forall (i : level) (A B : U i) (f g : finite_map A B) (a : A) (b : B) .
lookup f a = Some b : option B
-> lookup (merge f g) a = Some b : option B
lookup_merge_right : forall (i : level) (A B : U i) (f g : finite_map A B) (a : A) .
lookup f a = None : option B
-> lookup (merge f g) a = lookup g a : option B
We can obtain other corollaries about finite maps using the above and extensional equality:
update_update : forall (i : level) (A B : U i) (f : finite_map A B) (a : A) (b b' : B) .
update (update f a b) a b' = update f a b' : finite_map A B
update_update_neq : forall
(i : level)
(A B : U i)
(f : finite_map A B)
(a : A)
(b : B)
(a' : A)
(b' : B) .
a != a' : A
-> update (update f a b) a' b'
= update (update f a' b') a b
: finite_map A B
remove_empty : forall (i : level) (A B : U i) (e : eqtest A) (a : A) .
remove (empty e) a = empty e : finite_map A B
remove_update : forall (i : level) (A B : U i) (f : finite_map A B) (a : A) (b : B) .
remove (update f a b) a = remove f a : finite_map A B
remove_update_neq : forall
(i : level)
(A B : U i)
(f : finite_map A B)
(a : A)
(b : B)
(a' : A) .
a != a' : A
-> remove (update f a b) a' = update (remove f a') a b : finite_map A B
remove_absent : forall (i : level) (A B : U i) (f : finite_map A B) (a : A) .
lookup f a = None : option B -> remove f a = f : finite_map A B
merge_empty_left : forall (i : level) (A B : U i) (e : eqtest A) (f : finite_map A B) .
merge (empty e) f = f : finite_map A B
merge_empty_right : forall (i : level) (A B : U i) (e : eqtest A) (f : finite_map A B) .
merge f (empty e) = f : finite_map A B
update_merge_left : forall (i : level) (A B : U i) (f g : finite_map A B) (a : A) (b : B) .
update (merge f g) a b = merge (update f a b) g : finite_map A B
update_merge_right : forall (i : level) (A B : U i) (f g : finite_map A B) (a : A) (b : B) .
lookup f a = None : option B
-> update (merge f g) a b = merge f (update g a b) : finite_map A B
remove_merge : forall (i : level) (A B : U i) (f g : finite_map A B) (a : A) .
remove (merge f g) a = merge (remove f a) (remove g a) : finite_map A B
The derived notion of membership is useful:
member : intersect (i : level) . forall (A B : U i) . finite_map A B -> A -> U i
= fn A B f a . exists (b : B) . lookup f a = Some b : option B
(2 implicit arguments)
remove_absent' : forall (i : level) (A B : U i) (f : finite_map A B) (a : A) .
not (member f a) -> remove f a = f : finite_map A B
lookup_merge_left' : forall (i : level) (A B : U i) (f g : finite_map A B) (a : A) .
member f a -> lookup (merge f g) a = lookup f a : option B
lookup_merge_right' : forall (i : level) (A B : U i) (f g : finite_map A B) (a : A) .
not (member f a) -> lookup (merge f g) a = lookup g a : option B
Finite maps have finite domains, but it is difficult to obtain them without a canonical ordering on the domain’s type. One version gives the list quotiented by rearrangement:
finite_map_domain_quotient : forall (i : level) (A B : U i) (f : finite_map A B) .
quotient (L L'
: { L : list A
| forall (a : A) . member f a <-> In A a L }) .
(forall (a : A) . In A a L <-> In A a L')
Another version returns the domain squashed:
finite_map_domain_squash : forall (i : level) (A B : U i) (f : finite_map A B) .
{ exists (L : list A) . forall (a : A) . member f a <-> In A a L }
We can define a generic class determining what it means to be a finite map:
FiniteMap : forall (i : level) (A B T : U i) .
T -> (T -> A -> option B) -> (T -> A -> B -> T) -> (T -> A -> T) -> U i
= fn i A B T emp look upd rem .
(forall (f g : T) .
(forall (a : A) . look f a = look g a : option B) -> f = g : T)
& (forall (a a' : A) . Decidable.decidable (a = a' : A))
& (forall (a : A) . look emp a = None : option B)
& (forall (f : T) (a : A) (b : B) . look (upd f a b) a = Some b : option B)
& (forall (f : T) (a : A) (b : B) (a' : A) .
a != a' : A -> look (upd f a b) a' = look f a' : option B)
& (forall (f : T) (a : A) . look (rem f a) a = None : option B)
& (forall (f : T) (a a' : A) .
a != a' : A -> look (rem f a) a' = look f a' : option B)
& unit
We can then prove properties about such maps similar to what we showed above for simple finite maps:
FiniteMap_look_emp : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T) .
FiniteMap i A B T emp look upd rem
-> forall (a : A) . look emp a = None : option B
FiniteMap_look_upd : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T) .
FiniteMap i A B T emp look upd rem
-> forall (f : T) (a : A) (b : B) .
look (upd f a b) a = Some b : option B
FiniteMap_look_upd_neq : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T) .
FiniteMap i A B T emp look upd rem
-> forall (f : T) (a : A) (b : B) (a' : A) .
a != a' : A -> look (upd f a b) a' = look f a' : option B
FiniteMap_look_rem : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T) .
FiniteMap i A B T emp look upd rem
-> forall (f : T) (a : A) . look (rem f a) a = None : option B
FiniteMap_look_rem_neq : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T) .
FiniteMap i A B T emp look upd rem
-> forall (f : T) (a a' : A) .
a != a' : A -> look (rem f a) a' = look f a' : option B
FiniteMap_upd_upd : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T) .
FiniteMap i A B T emp look upd rem
-> forall (f : T) (a : A) (b b' : B) .
upd (upd f a b) a b' = upd f a b' : T
FiniteMap_upd_upd_neq : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T) .
FiniteMap i A B T emp look upd rem
-> forall (f : T) (a : A) (b : B) (a' : A) (b' : B) .
a != a' : A
-> upd (upd f a b) a' b' = upd (upd f a' b') a b : T
FiniteMap_rem_emp : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T) .
FiniteMap i A B T emp look upd rem
-> forall (a : A) . rem emp a = emp : T
FiniteMap_rem_upd : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T) .
FiniteMap i A B T emp look upd rem
-> forall (f : T) (a : A) (b : B) . rem (upd f a b) a = rem f a : T
FiniteMap_rem_upd_neq : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T) .
FiniteMap i A B T emp look upd rem
-> forall (f : T) (a : A) (b : B) (a' : A) .
a != a' : A -> rem (upd f a b) a' = upd (rem f a') a b : T
FiniteMap_rem_absent : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T) .
FiniteMap i A B T emp look upd rem
-> forall (f : T) (a : A) .
look f a = None : option B -> rem f a = f : T
Note that generic finite maps require that the domain’s equality is
decidable, and thus an equality test is not supplied to the emp
operation.
The FiniteMap
class does not include a merge operation. The
FiniteMapMerge
class adds one:
FiniteMapMerge : forall (i : level) (A B T : U i) .
T
-> (T -> A -> option B)
-> (T -> A -> B -> T)
-> (T -> A -> T)
-> (T -> T -> T)
-> U i
= fn i A B T emp look upd rem mer .
FiniteMap i A B T emp look upd rem
& (forall (f g : T) (a : A) (b : B) .
look f a = Some b : option B -> look (mer f g) a = Some b : option B)
& (forall (f g : T) (a : A) .
look f a = None : option B -> look (mer f g) a = look g a : option B)
& unit
FiniteMapMerge_look_mer_left : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T)
(mer : T -> T -> T) .
FiniteMapMerge i A B T emp look upd rem mer
-> forall (f g : T) (a : A) (b : B) .
look f a = Some b : option B
-> look (mer f g) a = Some b : option B
FiniteMapMerge_look_mer_right : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T)
(mer : T -> T -> T) .
FiniteMapMerge i A B T emp look upd rem mer
-> forall (f g : T) (a : A) .
look f a = None : option B
-> look (mer f g) a = look g a : option B
FiniteMapMerge_mer_emp_left : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T)
(mer : T -> T -> T) .
FiniteMapMerge i A B T emp look upd rem mer
-> forall (f : T) . mer emp f = f : T
FiniteMapMerge_mer_emp_right : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T)
(mer : T -> T -> T) .
FiniteMapMerge i A B T emp look upd rem mer
-> forall (f : T) . mer f emp = f : T
FiniteMapMerge_upd_mer_left : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T)
(mer : T -> T -> T) .
FiniteMapMerge i A B T emp look upd rem mer
-> forall (f g : T) (a : A) (b : B) .
upd (mer f g) a b = mer (upd f a b) g : T
FiniteMapMerge_upd_mer_right : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T)
(mer : T -> T -> T) .
FiniteMapMerge i A B T emp look upd rem mer
-> forall (f g : T) (a : A) (b : B) .
look f a = None : option B
-> upd (mer f g) a b = mer f (upd g a b) : T
FiniteMapMerge_rem_mer : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T)
(mer : T -> T -> T) .
FiniteMapMerge i A B T emp look upd rem mer
-> forall (f g : T) (a : A) .
rem (mer f g) a = mer (rem f a) (rem g a) : T
To assist in building finite maps, there is a class that leaves out extensionality equality:
PreFiniteMap : forall (i : level) (A B T : U i) .
T -> (T -> A -> option B) -> (T -> A -> B -> T) -> (T -> A -> T) -> U i
= fn i A B T emp look upd rem .
(forall (a a' : A) . Decidable.decidable (a = a' : A))
& (forall (a : A) . look emp a = None : option B)
& (forall (f : T) (a : A) (b : B) . look (upd f a b) a = Some b : option B)
& (forall (f : T) (a : A) (b : B) (a' : A) .
a != a' : A -> look (upd f a b) a' = look f a' : option B)
& (forall (f : T) (a : A) . look (rem f a) a = None : option B)
& (forall (f : T) (a a' : A) .
a != a' : A -> look (rem f a) a' = look f a' : option B)
& unit
Given a pre-finite-map, one can build a finite map by quotienting it:
(* "quotient pre-finite-map" *)
qpfm : intersect (i : level) . forall (A B T : U i) . (T -> A -> option B) -> U i
= fn A B T look . quotient (f g : T) . (forall (a : A) . look f a = look g a : option B)
quotient_emp : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T) .
PreFiniteMap i A B T emp look upd rem -> emp : qpfm A B T look
quotient_look : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T) .
PreFiniteMap i A B T emp look upd rem
-> look : qpfm A B T look -> A -> option B
quotient_upd : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T) .
PreFiniteMap i A B T emp look upd rem
-> upd : qpfm A B T look -> A -> B -> qpfm A B T look
quotient_rem : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T) .
PreFiniteMap i A B T emp look upd rem
-> rem : qpfm A B T look -> A -> qpfm A B T look
FiniteMap_qpfm : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T) .
PreFiniteMap i A B T emp look upd rem
-> FiniteMap i A B (qpfm A B T look) emp look upd rem
Another class gives finite maps with merge but without extensionality:
PreFiniteMapMerge : forall (i : level) (A B T : U i) .
T
-> (T -> A -> option B)
-> (T -> A -> B -> T)
-> (T -> A -> T)
-> (T -> T -> T)
-> U i
= fn i A B T emp look upd rem mer .
PreFiniteMap i A B T emp look upd rem
& (forall (f g : T) (a : A) (b : B) .
look f a = Some b : option B
-> look (mer f g) a = Some b : option B)
& (forall (f g : T) (a : A) .
look f a = None : option B
-> look (mer f g) a = look g a : option B)
& unit
quotient_mer : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T)
(mer : T -> T -> T) .
PreFiniteMapMerge i A B T emp look upd rem mer
-> mer : qpfm A B T look -> qpfm A B T look -> qpfm A B T look
FiniteMapMerge_qpfm : forall
(i : level)
(A B T : U i)
(emp : T)
(look : T -> A -> option B)
(upd : T -> A -> B -> T)
(rem : T -> A -> T)
(mer : T -> T -> T) .
PreFiniteMapMerge i A B T emp look upd rem mer
-> FiniteMapMerge i A B (qpfm A B T look) emp look upd rem mer
The simple finite maps are defined using these tools. The discrepancy
between the types of empty
versus emp
is mediated using a lemma
that extracts an equality test from a finite map:
finite_map_impl_eqtest : intersect (i : level) .
forall (A B : U i) . finite_map A B -> eqtest A
(By definition, all equality tests at the same type are equal.)