Istari

Union: elimination forms for union and iexists

To assist in typechecking code that operates on a union, we have unpack and its dependently typed analogue unpack_dep:

unpack     : intersect (i : level) (a : U i) (b : a -> U i) (c : U i) .
                (union (x : a) . b x) -> (intersect (x : a) . b x -> c) -> c
           = fn u f . f u

unpack_dep : intersect (i : level) (a : U i) (b : a -> U i) .
                forall (c : intersect (x : a) . b x -> U i) (u : union (x : a) . b x) .
                  (intersect (x : a) . forall (y : b x) . c y) -> c u
           = fn c u f . f u

The syntactic sugar unpack x = u in m is accepted for `unpack u (fn x . m).

Since unions are, at least in part, for managing situations in which some variables are hidden, everything in this module takes every argument invisibly (i.e., using a intersect) that can be taken that way.

Beyond that, observe that in the body of unpack and unpack_dep, the union’s hidden witness (x) is bound using intersection, which means that the body cannot use that variable.

This is essential, due to the nature of union types. Nevertheless, that limitation can be problematic when defining a type based on a union, so we have a variation on unpack for defining types:

unpackt : intersect (i : level) .
             forall (a : U i) (b : a -> U i) .
               (forall (x : a) . b x -> U i) -> (union (x : a) . b x) -> U i
        = fn a b c u .
             union (x : a) . exists (y : b x) . y = u : (union (x1 : a) . b x1) & c x y

The syntactic sugar unpackt (x , y) = u in b is accepted for `unpackt _ _ (fn x y . b) u.

The unpackt type has an introduction and elimination form:

unpackt_intro : intersect
                   (i : level)
                   (a : U i)
                   (b : a -> U i)
                   (c : forall (x : a) . b x -> U i)
                   (x : a) .
                   forall (y : b x) . c x y -> `unpackt a b c y
              = fn y z . (y, (), z)

unpackt_elim  : intersect
                   (i : level)
                   (a : U i)
                   (b : a -> U i)
                   (c : forall (x : a) . b x -> U i)
                   (d : (union (x : a) . b x) -> U i)
                   (u : union (x : a) . b x) .
                   `unpackt a b c u
                   -> (intersect (x : a) . forall (y : b x) . c x y -> d y)
                   -> d u
              = fn w f . f (w #1) (w #2 #2)

Again, observe that the continuation in unpackt_elim (i.e., its final argument) cannot refer to the union’s hidden witness (x).

The intro and elim forms reduce in the expected manner:

unpackt_elim (unpackt_intro y z) f --> f y z

To use an unpackt, it is generally just a matter of destructing it and exploiting its equality. Suppose:

i : level
a : U i
b : a -> U i
c : forall (x : a) . b x -> U i
d : forall (x : a) . b x -> U i
u : union (x : a) . b x
H : unpackt (x , y) = u in c x y

Then destruct /H/ /x y Heq Hy/ >> reduce /Hy/ produces:

x (hidden) : a
y : b x
Heq : y = u : (union (x1 : a) . b x1)
Hy : c x y

Now (assuming u is always used in a manner consistent with the type union (x : a) . b x) you can use subst /u/ to replace u with y and proceed from there.


A similar set of definitions is available for iexists:

iunpack     : intersect (i : level) (a : Kind i) (b : a -> U i) (c : U i) .
                 (iexists (x : a) . b x) -> (intersect (x : a) . b x -> c) -> c
            = fn u f . f u

iunpack_dep : intersect (i : level) (a : Kind i) (b : a -> U i) .
                 forall (c : intersect (x : a) . b x -> U i) (u : iexists (x : a) . b x) .
                   (intersect (x : a) . forall (y : b x) . c y) -> c u
            = fn c u f . f u

The syntactic sugar iunpack x = u in m is accepted for `iunpack u (fn x . m).

iunpackt : forall (i : level) (a : Kind i) (b : a -> U i) .
              (forall (x : a) . b x -> U i) -> (iexists (x : a) . b x) -> U i
         = fn i a b c u .
              iexists (x : a) . exists (y : b x) . y = u : (iexists (x1 : a) . b x1) & c x y

The syntactic sugar iunpackt (x , y) = u in b is accepted for `iunpackt _ _ _ (fn x y . b) u.

iunpackt_intro : intersect
                    (i : level)
                    (a : Kind i)
                    (b : a -> U i)
                    (c : forall (x : a) . b x -> U i)
                    (x : a) .
                    forall (y : b x) . c x y -> `iunpackt i a b c y
               = fn y z . (y, (), z)

iunpackt_elim  : intersect
                    (i : level)
                    (a : Kind i)
                    (b : a -> U i)
                    (c : forall (x : a) . b x -> U i)
                    (d : (iexists (x : a) . b x) -> U i)
                    (u : iexists (x : a) . b x) .
                    `iunpackt i a b c u
                    -> (intersect (x : a) . forall (y : b x) . c x y -> d y)
                    -> d u
               = fn w f . f (w #1) (w #2 #2)

iunpackt_elim (iunpackt_intro y z) f --> f y z