Search code examples
coq

Overcoming the need of having a constructor in the definition of an other constructor


Most probably I'm trying to achieve something flawed, but I'm running into a need to have a constructor in the definition of another constructor.

Silly example:

Inductive silly_type : Type :=
| constr1 : nat -> silly_type
| constr2 : forall (x : silly_type), (exists n : nat, (x = constr1 n)) -> silly_type.

There are two ways how to create elements of silly_type, either with natural number and constr1 or using constr2 and element of silly_type that was created with constr1.

The above definition of silly_type is not valid in coq, but I do not know how to overcome this in my less silly example.

Less silly example:

I'm trying to have an inductive type expr representing typed expressions.

Let's first start with an inductive type representing types:

Inductive type : Type :=
| base_type  : nat -> type
| arrow_type : type -> type -> type
| iso_arrow_type   : type -> type -> type.

There are some base types indexed with integers, arrow_type representing function between types and iso_arrow_type representing invertible functions between types.

Notation:

Notation "A --> B" := (arrow_type A B)   (at level 30, right associativity).
Notation "A <-> B" := (iso_arrow_type A B).

Type for expressions:

Inductive expr : type -> Type :=
| var  : forall (X : type) (n : nat), expr X
| eval : forall {X Y : type} (f : expr (X --> Y)) (x : expr X), expr Y
| inv  : forall {X Y : type}, expr ((X <-> Y) --> (Y <-> X))
| to_fun : forall {X Y : type}, expr ((X <-> Y) --> (X --> Y))
| to_iso : forall {X Y : type} (f : expr (X --> Y)), (is_invertible f) -> expr (Y --> X).

You can create an indexed variables of any type with var, evaluate a function with eval, invert an invertible function with inv, cast an invertible function to the actual function with to_fun such that you can evaluate it with eval.

The problem is with the last constructor to_iso. Sometimes, I will know that a function(element of X --> Y) is actually invertible and I want some mechanism to cast it to the type X <-> Y.

However, to define is_invertible I think that I need to use eval. This leads me to the problem that the constructor eval needs to be in the definition of the constructor to_iso.


To define is_invertible, we first need to define a notion of equality of expr:

Inductive ExprEq : forall (X : type),  X ->  X -> Prop :=
| expr_eq_refl : forall {X : type}, forall (x : X), ExprEq X x x
| eq_on_values : forall {X Y : type}, forall (f g : (X --> Y)), (forall (x : X), f[x] == g[x]) -> f == g
where 
"x == y" := (ExprEq _ x y) and "f [ x ]" := (eval f x) and "x =/= y" := (not (ExprEq _ x y)).

Thus, we have standard reflexivity x == x and equality of functions f==g can be decided by equality on all elements f[x]==g[x].

Now, we are ready to define is_invertible:

Definition is_invertible {X Y : type} (f : X --> Y) : Prop := exists g : Y --> X, (forall x, g[f[x]] == x) /\ (forall y, f[g[y]] == y).

This definition of is_invertible is problematic, it is using eval(f[x] := (eval f x)). Furthermore, the problem is a bit more complicated. We need to define the type ExprEq that already uses eval to define its constructors.

Constrain: keep expr decidable

What I really want to preserve is that the equality(=) of expr is decidable, i.e. being able to prove

Lemma eq_expr_dec {X : type} (x y : type) : {x=y} + {x<>y}.

I really do not care about decidability of the defined equality ==, but decidability of = is important for me.


Solution

  • You can use dependent types to index your constructors. For example

    Inductive silly_type : nat -> Type :=
    | constr1 : nat -> silly_type 0
    | constr2 : silly_type 0 -> silly_type 1.
    

    So you can only use values produced by constr1 in constr2. This approach should work in general to distinguish values created with different constructors.