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.
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.
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.
expr
decidableWhat 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.
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.