Usually, once a type has been defined, maps between the representation
type and the new type need to be proved. This may be accomplished using
define_new_type_bijections. In the example, the two functions are named
abs3 and rep3.
- val three_bij = define_new_type_bijections
{name="three_tybij", ABS="abs3", REP="rep3", tyax=tyax};
> val three_bij =
|- (!a. abs3 (rep3 a) = a) /\
(!r. (\(x,y). ~(x /\ y)) r = (rep3 (abs3 r) = r))
Properties of the maps may be conveniently proved with
prove_abs_fn_one_one, prove_abs_fn_onto, prove_rep_fn_one_one, and
prove_rep_fn_onto. In this case, we need only prove_abs_fn_one_one.
- val abs_11 = GEN_BETA_RULE (prove_abs_fn_one_one three_bij);
> val abs_11 =
|- !r r'.
~(FST r /\ SND r) ==>
~(FST r' /\ SND r') ==>
((abs3 r = abs3 r') = (r = r')) : thm
Now we address how to define constants designating the three elements
of our example type. We will use new_specification to create these
constants (say e1, e2, and e3) and their characterizing property,
which is
~(e1 = e2) /\ ~(e2 = e3) /\ ~(e3 = e1)
A simple lemma stating that the abstraction function doesn’t confuse any
of the representations is required:
- val abs_distinct =
REWRITE_RULE (PAIR_EQ::pair_rws)
(LIST_CONJ (map (C Q.SPECL abs_11)
[[`(F,F)`,`(F,T)`],
[`(F,T)`,`(T,F)`],
[`(T,F)`,`(F,F)`]]));
> val abs_distinct =
|- ~(abs3 (F,F) = abs3 (F,T)) /\
~(abs3 (F,T) = abs3 (T,F)) /\
~(abs3 (T,F) = abs3 (F,F)) : thm
Finally, we can introduce the constants and their property.
- val THREE = new_specification
("THREE", ["e1", "e2", "e3"],
PROVE [abs_distinct]
(Term`?x y z:three. ~(x=y) /\ ~(y=z) /\ ~(z=x)`));
> val THREE = |- ~(e1 = e2) /\ ~(e2 = e3) /\ ~(e3 = e1) : thm